diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 1ce6250b3..000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "libs/mangle"] - path = libs/mangle - url = git://github.com/zinnschlag/mangle.git -[submodule "libs/openengine"] - path = libs/openengine - url = git://github.com/zinnschlag/OpenEngine diff --git a/CMakeLists.txt b/CMakeLists.txt index 370b499e6..6dbd27a68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,6 +255,13 @@ if (APPLE) "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) # prepare plugins + if (${CMAKE_BUILD_TYPE} MATCHES "Release" OR + ${CMAKE_BUILD_TYPE} MATCHES "RelWithDebugInfo") + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + else() + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) + endif() + foreach(plugin ${USED_OGRE_PLUGINS}) configure_file("${OGRE_PLUGIN_DIR}/${plugin}.dylib" "${APP_BUNDLE_DIR}/Contents/Plugins/${plugin}.dylib" @@ -265,8 +272,7 @@ endif (APPLE) # Compiler settings if (CMAKE_COMPILER_IS_GNUCC) - #add_definitions (-Wall -Werror) - add_definitions (-Wall) + add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-reorder) endif (CMAKE_COMPILER_IS_GNUCC) if(DPKG_PROGRAM) @@ -343,6 +349,9 @@ if(WIN32) SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe") + SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") + SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") + # SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") if(EXISTS ${VCREDIST32}) @@ -402,6 +411,45 @@ if (WIN32) set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") + + # Play a bit with the warning levels + + set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them + + set(WARNINGS_DISABLE + # Warnings that aren't enabled normally and don't need to be enabled + # They're unneeded and sometimes completely retarded warnings that /Wall enables + # Not going to bother commenting them as they tend to warn on every standard library files + 4061 4263 4264 4266 4350 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946 + + # Warnings that are thrown on standard libraries and not OpenMW + 4347 # Non-template function with same name and parameter count as template function + 4365 # Variable signed/unsigned mismatch + 4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base + 4706 # Assignment in conditional expression + 4738 # Storing 32-bit float result in memory, possible loss of performance + 4986 # Undocumented warning that occurs in the crtdbg.h file + 4996 # Function was declared deprecated + + # OpenMW specific warnings + 4099 # Type mismatch, declared class or struct is defined with other type + 4100 # Unreferenced formal parameter (-Wunused-parameter) + 4127 # Conditional expression is constant + 4242 # Storing value in a variable of a smaller type, possible loss of data + 4244 # Storing value of one type in variable of another (size_t in int, for example) + 4305 # Truncating value (double to float, for example) + 4309 # Variable overflow, trying to store 128 in a signed char for example + 4355 # Using 'this' in member initialization list + 4701 # Potentially uninitialized local variable used + ) + + foreach(d ${WARNINGS_DISABLE}) + set(WARNINGS "${WARNINGS} /wd${d}") + endforeach(d) + + set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif(MSVC) # Same for MinGW diff --git a/README_Mac.md b/README_Mac.md index 39382e0c9..dc3918368 100644 --- a/README_Mac.md +++ b/README_Mac.md @@ -1,80 +1,79 @@ -NOTE: This README is for ardekantur's Mac branch of OpenMW. A README -for the main branch has yet to be written. If you want to submit one, -please send me a message! +#Getting OpenMW Working on OS X -OpenMW -====== +## Initial setup +First of all, clone OpenMW repo. -From the [official website][]: + $ git clone github.com/zinnschlag/openmw -> OpenMW is an attempt to reimplement the popular role playing game - Morrowind. It aims to be a fully playable, open source - implementation of the game. You must own Morrowind to use OpenMW. +Or use your github url if you forked. +About dependencies: I prefer not to install them globally (i. e. in /usr/local/), so I'm installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs. -About This Project ------------------- +It's useful to create env var for lib install prefix: + + $ export OMW_LIB_PREFIX=$HOME/path/libs/root` -This specific repository is a branch of OpenMW intended to keep pace -with development of the project in order to provide a Mac build for -interested parties to contribute. This is not an official, sanctioned -branch of the OpenMW project. I will only be able to answer specific -questions about getting this project running on Mac OS X, **no other -platform**. I will not even be able to guarantee my changes maintain -backwards compatibility against builds in other operating systems. You -have been warned. +Most of libs can be installed from [Homebrew][homebrew]. Only mpg123 needs to be installed from source (due to lack of universal compilation support). I think that some of libs can be installed from MacPorts or Fink too. +As OpenMW currently only supports i386 architecture on OS X, denendencies also should support it. Set some env vars in current terminal: -Getting OpenMW Working ----------------------- - -1. Clone this repository. -2. Note about libs: I prefer not to install them globally (i. e. in /usr/local/), so I installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs. - It's useful to create env var for lib install prefix: - $ export OMW_LIB_PREFIX=$HOME/path/libs/root - -3. First of all, set for current terminal some env vars: $ export CFLAGS="-arch i386" $ export CXXFLAGS="-arch i386" $ export LDFLAGS="-arch i386" - All libs will build with correct architecture. - If you close your terminal, you should set env vars again before pcoceeding to next steps! -4. Download [boost][] (tested with 1.45) and install it with the following command: +If you close your terminal, you should set env vars again before pcoceeding to next steps! + +## Boost +Download [boost][boost] and install it with the following command: $ cd /path/to/boost/source $ ./bootstrap.sh --prefix=$OMW_LIB_PREFIX $ ./bjam --build-dir=build --layout=versioned \ --toolset=darwin architecture=x86 address-model=32 \ --link-shared,static --prefix=$OMW_LIB_PREFIX install + + +Alternatively you can install boost with homebrew: + $ brew install boost --universal -5. Download [Ogre][] SDK (tested with 1.7.2), unpack it and move -`lib/Release/Ogre.framework` into `Library/Frameworks`. +I think MacPorts also should support universal build for boost. -6. Download [OIS][] and use the XCode project provided in - `ois/Mac/XCode-2.2`. Be sure to set your build architecture to - `i386` and your SDK platform to either 10.5 or 10.6. Once it - builds, move `ois/Mac/XCode-2.2/build/Debug/OIS.framework` to - `/Library/Frameworks`. +## Ogre +Download [Ogre][] SDK (tested with 1.7.3), unpack it somewhere and move +`lib/Release/Ogre.framework` into `/Library/Frameworks`. + +## OIS +Download patched [OIS][] and use the XCode project provided. Be sure to set your build architecture to + `i386`. Once it built, locate built OIS.framework with Xcode and move it to `/Library/Frameworks`. + +## mpg123 +Download [MPG 123][mpg123] and build it: -7. Download [mpg123][] and build it: $ cd /path/to/mpg123/source $ ./configure --prefix=$OMW_LIB_PREFIX --disable-debug \ --disable-dependency-tracking \ --with-optimization=4 \ - --with-audio=coreaudio \ - --with-default-audio=coreaudio \ + --with-audio=dummy \ + --with-default-audio=dummy \ --with-cpu=sse_alone \ $ make install -8. Download [libsndfile][] and build it: +## libsndfile +Download [libsndfile][] and build it: + $ cd /path/to/libsndfile/source $ ./configure --prefix=$OMW_LIB_PREFIX \ --disable-dependency-tracking $ make install -9. Download [Bullet][] and build it: +or install with homebrew: + + $ brew install libsndfile --universal + +## Bullet +Download [Bullet][] and build it: + $ cd /path/to/bullet/source $ mkdir build $ cd build @@ -87,12 +86,25 @@ Getting OpenMW Working -G"Unix Makefiles" ../ $ make install -10. Generate the Makefile for OpenMW as follows and build OpenMW: +or install with homebrew: + + $ brew install bullet --HEAD --universal + +I prefer head because 2.79 has some issue which causes OpenMW to lag. Also you can edit formula and install 2.77, which is stable and haven't mentioned issue. + +## Qt +Install [Qt][qt]. Qt SDK distributed by Nokia is not an option because it's 64 bit only, and OpenMW currently doesn't build for 64 bit on OS X. I'm installing it from Homebrew: + + $ brew install qt --universal + +## Run CMake +Generate the Makefile for OpenMW as follows and build OpenMW: + $ mkdir /path/to/openmw/build/dir $ cd /path/to/open/build/dir $ cmake \ -D CMAKE_OSX_ARCHITECTURES=i386 \ - -D OGRESDK=/path/to/ogre/sdk \ + -D OGRE_SDK=/path/to/ogre/sdk \ -D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \ -D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \ -D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ @@ -106,27 +118,43 @@ Getting OpenMW Working -D BULLET_INCLUDE_DIR=$OMW_LIB_PREFIX/include/bullet/ \ -G "Unix Makefiles" /path/to/openmw/source/dir $ make - You can use -G"Xcode" if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles" - if you prefer Eclipse. You also can specify -D CMAKE_BUILD_TYPE=Debug for debug - build. + +You can use `-G"Xcode"` if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles" +if you prefer Eclipse. You also can specify `-D CMAKE_BUILD_TYPE=Debug` for debug +build. As for CMake 2.8.7 and Xcode 4.3, Xcode generator is broken. Sadly Eclipse CDT also cannot import generated project at least on my machine. -11. Copy your Morrowind `Data Files` directory into the OpenMW build dir - with the name `data` or create symlink: - $ ln -s /path/to/morrowind/data/files /path/to/openmw/build/dir/data +If all libs installed via homebrew (excluding mpg123), then command would be even simplier: + + $ cmake \ + -D CMAKE_OSX_ARCHITECTURES="i386" \ + -D OGRE_SDK=/path/to/ogre/sdk \ + -D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \ + -D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ + -G "Unix Makefiles" /path/to/openmw/source/dir + $ make + +Note for users with recent Xcode versions: you must explicitly specify what set of compilers do you use! If not, gcc will be used for C and Clang for C++. Just add this two -D's to command: `-D CMAKE_C_COMPILER=/usr/bin/clang` and `-D CMAKE_CXX_COMPILER=/usr/bin/clang` + +Note for Xcode 4.3 users: you should specify full path to used SDK, because current CMake (2.8.7) couldn't find SDKs inside Xcode app bundle: + + -D CMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk" + +# Run +From your build directory run: -12. From your build directory run: $ OpenMW.app/Contents/MacOS/openmw - or: +or: + $ open OpenMW.app - Enjoy! - - +Enjoy! +[homebrew]: https://github.com/mxcl/homebrew [boost]: http://www.boost.org [Ogre]: http://www.ogre3d.org [Bullet]: http://bulletphysics.org -[OIS]: http://wgois.sf.net +[OIS]: https://github.com/corristo/ois-fork [mpg123]: http://www.mpg123.de [libsndfile]: http://www.mega-nerd.com/libsndfile [official website]: http://openmw.com [Will Thimbleby's Ogre Framework]: http://www.thimbleby.net/ogre/ +[qt]: http://qt.nokia.com/ \ No newline at end of file diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index f0e5eba94..a34ad7429 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -41,10 +41,11 @@ source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC find_package(Qt4 REQUIRED) set(QT_USE_QTGUI 1) -if (NOT APPLE) # this dependency can be completely removed, but now it only tested on OS X - find_package(PNG REQUIRED) - include_directories(${PNG_INCLUDE_DIR}) -endif() +# Set some platform specific settings +if(WIN32) + set(GUI_TYPE WIN32) + set(QT_USE_QTMAIN TRUE) +endif(WIN32) QT4_ADD_RESOURCES(RCC_SRCS resources.qrc) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) @@ -53,6 +54,7 @@ include(${QT_USE_FILE}) # Main executable add_executable(omwlauncher + ${GUI_TYPE} ${LAUNCHER} ${RCC_SRCS} ${MOC_SRCS} @@ -62,7 +64,6 @@ target_link_libraries(omwlauncher ${Boost_LIBRARIES} ${OGRE_LIBRARIES} ${QT_LIBRARIES} - ${PNG_LIBRARY} components ) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index c95446808..054cbf141 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -9,6 +9,23 @@ #include "pluginsmodel.hpp" #include "pluginsview.hpp" +#include +/** + * Workaround for problems with whitespaces in paths in older versions of Boost library + */ +#if (BOOST_VERSION <= 104600) +namespace boost +{ + + template<> + inline boost::filesystem::path lexical_cast(const std::string& arg) + { + return boost::filesystem::path(arg); + } + +} /* namespace boost */ +#endif /* (BOOST_VERSION <= 104600) */ + using namespace ESM; using namespace std; diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 39cd99cf6..77f465b4c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -44,7 +44,7 @@ add_openmw_dir (mwsound add_openmw_dir (mwworld refdata world physicssystem scene environment globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellfunctors - cells localscripts customdata weather + cells localscripts customdata weather inventorystore ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5322cb90d..490587b9f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -118,8 +118,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // sound if (mUseSound) { - if (!mEnvironment.mSoundManager->isMusicPlaying()) - mEnvironment.mSoundManager->startRandomTitle(); + mEnvironment.mSoundManager->playPlaylist(); mEnvironment.mSoundManager->update (evt.timeSinceLastFrame); } @@ -340,7 +339,6 @@ void OMW::Engine::go() // Create sound system mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(), mOgre->getCamera(), - mEnvironment.mWorld->getStore(), mDataDirs, mUseSound, mFSStrict, mEnvironment); @@ -389,7 +387,7 @@ void OMW::Engine::go() mOgre->getRoot()->addFrameListener (this); // Play some good 'ol tunes - mEnvironment.mSoundManager->startRandomTitle(); + mEnvironment.mSoundManager->playPlaylist(std::string("Explore")); // scripts if (mCompileAll) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index da7ff8696..e95fb572f 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -53,6 +56,8 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -71,4 +76,14 @@ namespace MWClass registerClass (typeid (ESM::Apparatus).name(), instance); } + + std::string Apparatus::getUpSoundId (const MWWorld::Ptr& ptr) const + { + return std::string("Item Apparatus Up"); + } + + std::string Apparatus::getDownSoundId (const MWWorld::Ptr& ptr) const + { + return std::string("Item Apparatus Down"); + } } diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 86223cf60..c0849e1fe 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -26,6 +26,12 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 3cdf63119..e1c2734f0 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -2,14 +2,22 @@ #include "armor.hpp" #include +#include +#include #include #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" + #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -52,6 +60,8 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -77,10 +87,105 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + const int size = 11; + + static const int sMapping[size][2] = + { + { ESM::Armor::Helmet, MWWorld::InventoryStore::Slot_Helmet }, + { ESM::Armor::Cuirass, MWWorld::InventoryStore::Slot_Cuirass }, + { ESM::Armor::LPauldron, MWWorld::InventoryStore::Slot_LeftPauldron }, + { ESM::Armor::RPauldron, MWWorld::InventoryStore::Slot_RightPauldron }, + { ESM::Armor::Greaves, MWWorld::InventoryStore::Slot_Greaves }, + { ESM::Armor::Boots, MWWorld::InventoryStore::Slot_Boots }, + { ESM::Armor::LGauntlet, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Armor::RGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet }, + { ESM::Armor::Shield, MWWorld::InventoryStore::Slot_CarriedLeft }, + { ESM::Armor::LBracer, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Armor::RBracer, MWWorld::InventoryStore::Slot_RightGauntlet } + }; + + for (int i=0; ibase->data.type) + { + slots.push_back (int (sMapping[i][1])); + break; + } + + return std::make_pair (slots, false); + } + + int Armor::getEquipmentSkill (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::string typeGmst; + + switch (ref->base->data.type) + { + case ESM::Armor::Helmet: typeGmst = "iHelmWeight"; break; + case ESM::Armor::Cuirass: typeGmst = "iCuirassWeight"; break; + case ESM::Armor::LPauldron: + case ESM::Armor::RPauldron: typeGmst = "iPauldronWeight"; break; + case ESM::Armor::Greaves: typeGmst = "iGreavesWeight"; break; + case ESM::Armor::Boots: typeGmst = "iBootsWeight"; break; + case ESM::Armor::LGauntlet: + case ESM::Armor::RGauntlet: typeGmst = "iGauntletWeight"; break; +/// \todo how to determine if shield light, medium or heavy? +// case ESM::Armor::Shield: + case ESM::Armor::LBracer: + case ESM::Armor::RBracer: typeGmst = "iGauntletWeight"; break; + } + + if (typeGmst.empty()) + return -1; + + float iWeight = environment.mWorld->getStore().gameSettings.find (typeGmst)->i; + + if (iWeight * environment.mWorld->getStore().gameSettings.find ("fLightMaxMod")->f>= + ref->base->data.weight) + return ESM::Skill::LightArmor; + + if (iWeight * environment.mWorld->getStore().gameSettings.find ("fMedMaxMod")->f>= + ref->base->data.weight) + return ESM::Skill::MediumArmor; + + return ESM::Skill::HeavyArmor; + } + void Armor::registerSelf() { boost::shared_ptr instance (new Armor); registerClass (typeid (ESM::Armor).name(), instance); } + + std::string Armor::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + int es = getEquipmentSkill(ptr, environment); + if (es == ESM::Skill::LightArmor) + return std::string("Item Armor Light Up"); + else if (es == ESM::Skill::MediumArmor) + return std::string("Item Armor Medium Up"); + else + return std::string("Item Armor Heavy Up"); + } + + std::string Armor::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + int es = getEquipmentSkill(ptr, environment); + if (es == ESM::Skill::LightArmor) + return std::string("Item Armor Light Down"); + else if (es == ESM::Skill::MediumArmor) + return std::string("Item Armor Medium Down"); + else + return std::string("Item Armor Heavy Down"); + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 060bc364e..2b66ff828 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -31,7 +31,22 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 576e521ee..0a81ebafb 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -55,6 +58,8 @@ namespace MWClass { // TODO implement reading + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -73,4 +78,14 @@ namespace MWClass registerClass (typeid (ESM::Book).name(), instance); } + + std::string Book::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Book Up"); + } + + std::string Book::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Book Down"); + } } diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 12dc27bb2..ccbbfb4b2 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -26,6 +26,12 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 88c43d82c..4fe19ada4 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -7,9 +7,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -53,6 +57,8 @@ namespace MWClass boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -65,10 +71,86 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + if (ref->base->data.type==ESM::Clothing::Ring) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_LeftRing)); + slots.push_back (int (MWWorld::InventoryStore::Slot_RightRing)); + } + else + { + const int size = 9; + + static const int sMapping[size][2] = + { + { ESM::Clothing::Shirt, MWWorld::InventoryStore::Slot_Cuirass }, + { ESM::Clothing::Belt, MWWorld::InventoryStore::Slot_Belt }, + { ESM::Clothing::Robe, MWWorld::InventoryStore::Slot_Robe }, + { ESM::Clothing::Pants, MWWorld::InventoryStore::Slot_Pants }, + { ESM::Clothing::Shoes, MWWorld::InventoryStore::Slot_Boots }, + { ESM::Clothing::LGlove, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Clothing::RGlove, MWWorld::InventoryStore::Slot_RightGauntlet }, + { ESM::Clothing::Skirt, MWWorld::InventoryStore::Slot_Skirt }, + { ESM::Clothing::Amulet, MWWorld::InventoryStore::Slot_Amulet } + }; + + for (int i=0; ibase->data.type) + { + slots.push_back (int (sMapping[i][1])); + break; + } + } + + return std::make_pair (slots, false); + } + + int Clothing::getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->data.type==ESM::Clothing::Shoes) + return ESM::Skill::Unarmored; + + return -1; + } + void Clothing::registerSelf() { boost::shared_ptr instance (new Clothing); registerClass (typeid (ESM::Clothing).name(), instance); } + + std::string Clothing::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->data.type == 8) + { + return std::string("Item Ring Up"); + } + return std::string("Item Clothes Up"); + } + + std::string Clothing::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->data.type == 8) + { + return std::string("Item Ring Down"); + } + return std::string("Item Clothes Down"); + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 606aba9e0..171b06246 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -25,7 +25,22 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index c58606996..c58a25c03 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -85,7 +85,7 @@ namespace MWClass { // TODO check for key std::cout << "Locked container" << std::endl; - environment.mSoundManager->playSound(lockedSound, 1.0, 1.0); + environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false); return boost::shared_ptr (new MWWorld::NullAction); } else @@ -100,7 +100,7 @@ namespace MWClass { // Trap activation goes here std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - environment.mSoundManager->playSound(trapActivationSound, 1.0, 1.0); + environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0, false); ptr.getCellRef().trap = ""; return boost::shared_ptr (new MWWorld::NullAction); } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index bd7af9597..5654dff69 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -73,7 +73,7 @@ namespace MWClass // TODO check for key // TODO report failure to player (message, sound?). Look up behaviour of original MW. std::cout << "Locked!" << std::endl; - environment.mSoundManager->playSound(lockedSound, 1.0, 1.0); + environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false); return boost::shared_ptr (new MWWorld::NullAction); } @@ -81,7 +81,7 @@ namespace MWClass { // Trap activation std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - environment.mSoundManager->playSound(trapActivationSound, 1.0, 1.0); + environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0, false); ptr.getCellRef().trap = ""; return boost::shared_ptr (new MWWorld::NullAction); } @@ -92,6 +92,7 @@ namespace MWClass if (environment.mWorld->getPlayer().getPlayer()==actor) { // the player is using the door + // The reason this is not 3D is that it would get interrupted when you teleport environment.mSoundManager->playSound(openSound, 1.0, 1.0); return boost::shared_ptr ( new MWWorld::ActionTeleportPlayer (ref->ref.destCell, ref->ref.doorDest)); @@ -109,7 +110,7 @@ namespace MWClass // TODO return action for rotating the door // This is a little pointless, but helps with testing - environment.mSoundManager->playSound(openSound, 1.0, 1.0); + environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0, false); return boost::shared_ptr (new MWWorld::NullAction); } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index d00e4592d..1a7edf632 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -51,6 +54,8 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -69,4 +74,14 @@ namespace MWClass registerClass (typeid (ESM::Ingredient).name(), instance); } + + std::string Ingredient::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Ingredient Up"); + } + + std::string Ingredient::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Ingredient Down"); + } } diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 92d2c4eef..9463dcf8d 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -26,6 +26,12 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index f9ec1c956..e2e63a89b 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -9,6 +9,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwsound/soundmanager.hpp" @@ -82,6 +83,8 @@ namespace MWClass if (!(ref->base->data.flags & ESM::Light::Carry)) return boost::shared_ptr (new MWWorld::NullAction); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -94,10 +97,33 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + if (ref->base->data.flags & ESM::Light::Carry) + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedLeft)); + + return std::make_pair (slots, false); + } + void Light::registerSelf() { boost::shared_ptr instance (new Light); registerClass (typeid (ESM::Light).name(), instance); } + + std::string Light::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Misc Up"); + } + + std::string Light::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Misc Down"); + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index c9940d0a5..46a4d60ba 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -30,7 +30,17 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 98c05a1b3..3dda2f4af 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -7,9 +7,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -54,6 +58,8 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -66,10 +72,29 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Lockpick::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + std::vector slots; + + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, false); + } + void Lockpick::registerSelf() { boost::shared_ptr instance (new Lockpick); registerClass (typeid (ESM::Tool).name(), instance); } + + std::string Lockpick::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Lockpick Up"); + } + + std::string Lockpick::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Lockpick Down"); + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 9cbfa0d23..0c9189c54 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -25,7 +25,17 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 8dde84be9..864fc1e38 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -53,6 +56,8 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -71,4 +76,28 @@ namespace MWClass registerClass (typeid (ESM::Miscellaneous).name(), instance); } + + std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->name =="Gold") + { + return std::string("Item Gold Up"); + } + return std::string("Item Misc Up"); + } + + std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->name =="Gold") + { + return std::string("Item Gold Down"); + } + return std::string("Item Misc Down"); + } } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index de01a64f4..b07964f99 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -26,6 +26,12 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index cc7daa83e..83a94d27d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -16,7 +16,7 @@ #include "../mwworld/actiontalk.hpp" #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" -#include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" namespace @@ -29,7 +29,7 @@ namespace MWMechanics::NpcStats mNpcStats; MWMechanics::CreatureStats mCreatureStats; MWMechanics::Movement mMovement; - MWWorld::ContainerStore mContainerStore; + MWWorld::InventoryStore mInventoryStore; virtual MWWorld::CustomData *clone() const; }; @@ -161,7 +161,15 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; + } + + MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr) + const + { + ensureCustomData (ptr); + + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; } std::string Npc::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index bef417332..f210eda5f 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -38,6 +38,9 @@ namespace MWClass virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store + virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; + ///< Return inventory store + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index c57c18fd1..4ab374590 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -53,6 +56,8 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -71,4 +76,14 @@ namespace MWClass registerClass (typeid (ESM::Potion).name(), instance); } + + std::string Potion::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Potion Up"); + } + + std::string Potion::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Potion Down"); + } } diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index fd78bba53..be9e713fb 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -26,6 +26,12 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index de024e430..4b4d79a73 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -7,9 +7,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -53,6 +57,8 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -65,10 +71,29 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Probe::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + std::vector slots; + + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, false); + } + void Probe::registerSelf() { boost::shared_ptr instance (new Probe); registerClass (typeid (ESM::Probe).name(), instance); } + + std::string Probe::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Probe Up"); + } + + std::string Probe::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Probe Down"); + } } diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 3f2bfed5b..1507d65aa 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -25,7 +25,17 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index f831b6b50..758bf4079 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -53,6 +56,8 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -71,4 +76,14 @@ namespace MWClass registerClass (typeid (ESM::Repair).name(), instance); } + + std::string Repair::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Repair Up"); + } + + std::string Repair::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Repair Down"); + } } diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index a5864ab35..17b606f4c 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -26,6 +26,12 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 90fd3e33b..20db0cf38 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -7,9 +7,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -53,6 +57,8 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -78,10 +84,157 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + bool stack = false; + + if (ref->base->data.type==ESM::Weapon::Arrow || ref->base->data.type==ESM::Weapon::Bolt) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_Ammunition)); + stack = true; + } + else if (ref->base->data.type==ESM::Weapon::MarksmanThrown) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + stack = true; + } + else + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, stack); + } + + int Weapon::getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + const int size = 12; + + static const int sMapping[size][2] = + { + { ESM::Weapon::ShortBladeOneHand, ESM::Skill::ShortBlade }, + { ESM::Weapon::LongBladeOneHand, ESM::Skill::LongBlade }, + { ESM::Weapon::LongBladeTwoHand, ESM::Skill::LongBlade }, + { ESM::Weapon::BluntOneHand, ESM::Skill::BluntWeapon }, + { ESM::Weapon::BluntTwoClose, ESM::Skill::BluntWeapon }, + { ESM::Weapon::BluntTwoWide, ESM::Skill::BluntWeapon }, + { ESM::Weapon::SpearTwoWide, ESM::Skill::Spear }, + { ESM::Weapon::AxeOneHand, ESM::Skill::Axe }, + { ESM::Weapon::AxeTwoHand, ESM::Skill::Axe }, + { ESM::Weapon::MarksmanBow, ESM::Skill::Marksman }, + { ESM::Weapon::MarksmanCrossbow, ESM::Skill::Marksman }, + { ESM::Weapon::MarksmanThrown, ESM::Skill::Marksman } + }; + + for (int i=0; ibase->data.type) + return sMapping[i][1]; + + return -1; + } + void Weapon::registerSelf() { boost::shared_ptr instance (new Weapon); registerClass (typeid (ESM::Weapon).name(), instance); } + + std::string Weapon::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + int type = ref->base->data.type; + // Ammo + if (type == 12 || type == 13) + { + return std::string("Item Ammo Up"); + } + // Bow + if (type == 9) + { + return std::string("Item Weapon Bow Up"); + } + // Crossbow + if (type == 10) + { + return std::string("Item Weapon Crossbow Up"); + } + // Longblades, One hand and Two + if (type == 1 || type == 2) + { + return std::string("Item Weapon Longblade Up"); + } + // Shortblade and thrown weapons + // thrown weapons may not be entirely correct + if (type == 0 || type == 11) + { + return std::string("Item Weapon Shortblade Up"); + } + // Spear + if (type == 6) + { + return std::string("Item Weapon Spear Up"); + } + // Blunts and Axes + if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8) + { + return std::string("Item Weapon Blunt Up"); + } + + return std::string("Item Misc Up"); + } + + std::string Weapon::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + int type = ref->base->data.type; + // Ammo + if (type == 12 || type == 13) + { + return std::string("Item Ammo Down"); + } + // Bow + if (type == 9) + { + return std::string("Item Weapon Bow Down"); + } + // Crossbow + if (type == 10) + { + return std::string("Item Weapon Crossbow Down"); + } + // Longblades, One hand and Two + if (type == 1 || type == 2) + { + return std::string("Item Weapon Longblade Down"); + } + // Shortblade and thrown weapons + // thrown weapons may not be entirely correct + if (type == 0 || type == 11) + { + return std::string("Item Weapon Shortblade Down"); + } + // Spear + if (type == 6) + { + return std::string("Item Weapon Spear Down"); + } + // Blunts and Axes + if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8) + { + return std::string("Item Weapon Blunt Down"); + } + + return std::string("Item Misc Down"); + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index b056249b9..f863c0bfe 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -31,7 +31,22 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 0e2e692d3..3fd6e7892 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -50,7 +50,7 @@ namespace MWGui return isGood(); } - catch (const Compiler::SourceException& error) + catch (const Compiler::SourceException&) { // error has already been reported via error handler } @@ -342,7 +342,7 @@ namespace MWGui if( ( matches.front().find(' ') != string::npos ) ) { if( !has_front_quote ) output.append(string("\"")); - return output.append(matches.front() + string("\" ")); + return output.append(matches.front() + string("\" ")); } else if( has_front_quote ) { return output.append(matches.front() + string("\" ")); @@ -361,7 +361,7 @@ namespace MWGui /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); return output; - } + } } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 63855f3b8..7b0d7015c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -111,7 +111,6 @@ namespace MWRender{ Nif::NiTriShapeCopy& copy = *allshapesiter; std::vector* allvertices = ©.vertices; - std::vector* allnormals = ©.normals; @@ -182,7 +181,6 @@ namespace MWRender{ std::vector inds = iter->second; int verIndex = iter->first; Ogre::Vector3 currentVertex = (*allvertices)[verIndex]; - Ogre::Vector3 currentNormal = (*allnormals)[verIndex]; Nif::NiSkinData::BoneInfoCopy* boneinfocopy = &(allshapesiter->boneinfo[inds[0].boneinfocopyindex]); Ogre::Bone *bonePtr = 0; @@ -276,6 +274,7 @@ namespace MWRender{ rotmult = bonePtr->getOrientation(); scale = bonePtr->getScale().x; boneSequenceIter++; + for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) { if(creaturemodel->getSkeleton()->hasBone(*boneSequenceIter)){ @@ -330,7 +329,7 @@ namespace MWRender{ } } - bool Animation::timeIndex( float time, std::vector times, int & i, int & j, float & x ){ + bool Animation::timeIndex( float time, const std::vector & times, int & i, int & j, float & x ){ int count; if ( (count = times.size()) > 0 ) { @@ -388,6 +387,8 @@ namespace MWRender{ } void Animation::handleAnimationTransforms(){ + + Ogre::SkeletonInstance* skel = base->getSkeleton(); @@ -404,10 +405,10 @@ namespace MWRender{ for(unsigned int i = 0; i < entityparts.size(); i++){ //Ogre::SkeletonInstance* skel = entityparts[i]->getSkeleton(); - Ogre::Bone* b = skel->getRootBone(); - b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3));//This is a trick + //Ogre::Bone* b = skel->getRootBone(); + //b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3));//This is a trick - entityparts[i]->getAllAnimationStates()->_notifyDirty(); + //entityparts[i]->getAllAnimationStates()->_notifyDirty(); } @@ -424,18 +425,19 @@ namespace MWRender{ float x; float x2; - std::vector quats = iter->getQuat(); + const std::vector & quats = iter->getQuat(); - std::vector ttime = iter->gettTime(); - std::vector::iterator ttimeiter = ttime.begin(); + const std::vector & ttime = iter->gettTime(); + + + const std::vector & rtime = iter->getrTime(); + int rindexJ = rindexI[slot]; - std::vector rtime = iter->getrTime(); - int rindexJ = 0; timeIndex(time, rtime, rindexI[slot], rindexJ, x2); - int tindexJ = 0; + int tindexJ = tindexI[slot]; - std::vector translist1 = iter->getTranslist1(); + const std::vector & translist1 = iter->getTranslist1(); timeIndex(time, ttime, tindexI[slot], tindexJ, x); @@ -443,34 +445,35 @@ namespace MWRender{ Ogre::Quaternion r; bool bTrans = translist1.size() > 0; - if(bTrans){ - Ogre::Vector3 v1 = translist1[tindexI[slot]]; - Ogre::Vector3 v2 = translist1[tindexJ]; - t = (v1 + (v2 - v1) * x); - } bool bQuats = quats.size() > 0; - if(bQuats){ - r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); - } - skel = base->getSkeleton(); + if(skel->hasBone(iter->getBonename())){ Ogre::Bone* bone = skel->getBone(iter->getBonename()); - if(bTrans) + if(bTrans){ + Ogre::Vector3 v1 = translist1[tindexI[slot]]; + Ogre::Vector3 v2 = translist1[tindexJ]; + t = (v1 + (v2 - v1) * x); bone->setPosition(t); - if(bQuats) + + } + if(bQuats){ + r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); bone->setOrientation(r); + } + - skel->_updateTransforms(); - base->getAllAnimationStates()->_notifyDirty(); } + slot++; } + skel->_updateTransforms(); + base->getAllAnimationStates()->_notifyDirty(); } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index bad7eca15..d1e8071f0 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -28,6 +28,7 @@ class Animation{ MWWorld::Environment& mEnvironment; std::map vecRotPos; static std::map mUniqueIDs; + std::vector* > shapeparts; //All the NiTriShape data that we need for animating an npc @@ -55,7 +56,7 @@ class Animation{ Ogre::Entity* base; void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); void handleAnimationTransforms(); - bool timeIndex( float time, std::vector times, int & i, int & j, float & x ); + bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); std::string getUniqueID(std::string mesh); public: @@ -65,7 +66,7 @@ class Animation{ void stopScript(); - ~Animation(); + virtual ~Animation(); }; } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index 179991442..2229eeec9 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -16,7 +16,7 @@ namespace MWRender{ class CreatureAnimation: public Animation{ public: - ~CreatureAnimation(); + virtual ~CreatureAnimation(); CreatureAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend); virtual void runAnimation(float timepassed); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 4c9c0e466..c6fe023d6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -42,6 +42,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env,O std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2); bool female = tolower(secondtolast) == 'f'; + std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; /*std::cout << "Race: " << ref->base->race ; @@ -276,6 +277,7 @@ void NpcAnimation::runAnimation(float timepassed){ shapepartsiter++; entitypartsiter++; } + } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index e8ce735f7..e2071957c 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -20,7 +20,7 @@ class NpcAnimation: public Animation{ public: NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend); - ~NpcAnimation(); + virtual ~NpcAnimation(); Ogre::Entity* insertBoundedPart(const std::string &mesh, std::string bonename); void insertFreePart(const std::string &mesh, const std::string suffix, Ogre::SceneNode* insert); virtual void runAnimation(float timepassed); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7b58a80d7..95eb8dd33 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -210,9 +210,14 @@ void RenderingManager::configureFog(ESMS::CellStore &mCell) void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) { /// \todo make the viewing distance and fog start/end configurable - float low = 3000 / density; - float high = 6200 / density; - + + // right now we load 3x3 cells, so the maximum viewing distance we + // can allow (to prevent objects suddenly popping up) equals: + // 8192 * 0.69 + // ^ cell size ^ minimum density value used (clear weather) + float low = 5652.48 / density / 2.f; + float high = 5652.48 / density; + mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high); mRendering.getCamera()->setFarClipDistance ( high ); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 1b324459a..e09fa8a42 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -59,7 +59,8 @@ void BillboardObject::setPosition(const Vector3& pPosition) Vector3 BillboardObject::getPosition() const { - return mNode->getPosition(); + Vector3 p = mNode->_getDerivedPosition() - mNode->getParentSceneNode()->_getDerivedPosition(); + return Vector3(p.x, -p.z, p.y); } void BillboardObject::setColour(const ColourValue& pColour) @@ -161,14 +162,20 @@ Moon::Moon( const String& textureName, " in float2 uv : TEXCOORD0, \n" " out float4 oColor : COLOR, \n" " uniform sampler2D texture : TEXUNIT0, \n" + " uniform float4 skyColour, \n" " uniform float4 diffuse, \n" " uniform float4 emissive \n" ") \n" "{ \n" " float4 tex = tex2D(texture, uv); \n" - " oColor = float4(emissive.xyz,1) * tex2D(texture, uv) * float4(1,1,1,diffuse.a); \n" - " float bump = pow((1-diffuse.a),4); \n" - " oColor.rgb += float3(bump, bump, bump)*0.5; \n" + " oColor = float4(emissive.xyz,1) * tex; \n" + // use a circle for the alpha (compute UV distance to center) + // looks a bit bad because its not filtered on the edges, + // but it's cheaper than a seperate alpha texture. + " float sqrUVdist = pow(uv.x-0.5,2) + pow(uv.y-0.5, 2); \n" + " oColor.a = diffuse.a * (sqrUVdist >= 0.24 ? 0 : 1); \n" + " oColor.rgb += (1-tex.a) * oColor.a * skyColour.rgb; \n"//fill dark side of moon with skycolour + " oColor.rgb += (1-diffuse.a) * skyColour.rgb; \n"//fade bump "}"; fshader->setSource(outStream2.str()); fshader->load(); @@ -186,15 +193,19 @@ void Moon::setType(const Moon::Type& type) mType = type; } +void Moon::setSkyColour(const Ogre::ColourValue& colour) +{ + mMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("skyColour", colour); +} -/// \todo the moon phase rendering is not correct - the dark part of the moon does not occlude the stars void Moon::setPhase(const Moon::Phase& phase) { + // Colour texture Ogre::String textureName = "textures\\tx_"; - + if (mType == Moon::Type_Secunda) textureName += "secunda_"; else textureName += "masser_"; - + if (phase == Moon::Phase_New) textureName += "new"; else if (phase == Moon::Phase_WaxingCrescent) textureName += "one_wax"; else if (phase == Moon::Phase_WaxingHalf) textureName += "half_wax"; @@ -203,9 +214,9 @@ void Moon::setPhase(const Moon::Phase& phase) else if (phase == Moon::Phase_WaningHalf) textureName += "half_wan"; else if (phase == Moon::Phase_WaningGibbous) textureName += "three_wan"; else if (phase == Moon::Phase_Full) textureName += "full"; - + textureName += ".dds"; - + mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(textureName); mPhase = phase; @@ -338,24 +349,11 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) : mAtmosphereNight = mRootNode->createChildSceneNode(); mAtmosphereNight->attachObject(night1_ent); - for (unsigned int i=0; igetNumSubEntities(); ++i) - { - MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); - mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); - mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); - mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0); - mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); - - mStarsMaterials[i] = mp; - } - // Stars vertex shader - HighLevelGpuProgramPtr vshader3 = mgr.createProgram("Stars_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + HighLevelGpuProgramPtr stars_vp = mgr.createProgram("Stars_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); - vshader3->setParameter("profiles", "vs_2_x arbvp1"); - vshader3->setParameter("entry_point", "main_vp"); + stars_vp->setParameter("profiles", "vs_2_x arbvp1"); + stars_vp->setParameter("entry_point", "main_vp"); StringUtil::StrStreamType outStream4; outStream4 << "void main_vp( \n" @@ -371,10 +369,9 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) : " oFade = (position.z > 50) ? 1.f : 0.f; \n" " oPosition = mul( worldViewProj, position ); \n" "}"; - vshader3->setSource(outStream4.str()); - vshader3->load(); - vshader3->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - night1_ent->getSubEntity(3)->getMaterial()->getTechnique(0)->getPass(0)->setVertexProgram(vshader3->getName()); + stars_vp->setSource(outStream4.str()); + stars_vp->load(); + stars_vp->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); // Stars fragment shader HighLevelGpuProgramPtr stars_fp = mgr.createProgram("Stars_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -399,7 +396,20 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) : stars_fp->load(); stars_fp->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); stars_fp->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - night1_ent->getSubEntity(3)->getMaterial()->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName()); + + for (unsigned int i=0; igetNumSubEntities(); ++i) + { + MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); + mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); + mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); + mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0); + mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mp->getTechnique(0)->getPass(0)->setVertexProgram(stars_vp->getName()); + mp->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName()); + mStarsMaterials[i] = mp; + } // Atmosphere (day) mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); @@ -583,6 +593,9 @@ void SkyManager::update(float duration) mSun->setVisible(mSunEnabled); mMasser->setVisible(mMasserEnabled); mSecunda->setVisible(mSecundaEnabled); + + // rotate the whole sky by 360 degrees every 4 days + mRootNode->roll(Degree(mHourDiff*360/96.f)); } void SkyManager::enable() @@ -647,6 +660,8 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) if (mSkyColour != weather.mSkyColor) { mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(weather.mSkyColor); + mMasser->setSkyColour(weather.mSkyColor); + mSecunda->setSkyColour(weather.mSkyColor); mSkyColour = weather.mSkyColor; } @@ -760,6 +775,9 @@ void SkyManager::setSecundaFade(const float fade) void SkyManager::setHour(double hour) { + mHourDiff = mHour - hour; + if (mHourDiff > 0) mHourDiff -= 24; + mHour = hour; } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 2678165e3..2c7b7379a 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -34,6 +34,8 @@ namespace MWRender Ogre::SceneNode* rootNode ); BillboardObject(); + + virtual ~BillboardObject() {} void setColour(const Ogre::ColourValue& pColour); void setPosition(const Ogre::Vector3& pPosition); @@ -69,6 +71,8 @@ namespace MWRender const Ogre::Vector3& position, Ogre::SceneNode* rootNode ); + + virtual ~Moon() {} enum Phase { @@ -90,6 +94,7 @@ namespace MWRender void setPhase(const Phase& phase); void setType(const Type& type); + void setSkyColour(const Ogre::ColourValue& colour); Phase getPhase() const; unsigned int getPhaseInt() const; @@ -162,7 +167,9 @@ namespace MWRender float mHour; int mDay; int mMonth; - + + float mHourDiff; + BillboardObject* mSun; BillboardObject* mSunGlare; Moon* mMasser; diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 6795eff19..a5ba04e26 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -10,10 +10,8 @@ #include #include -#include #include - #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" #include "../mwworld/player.hpp" @@ -45,7 +43,6 @@ using namespace Mangle::Sound; typedef OEngine::Sound::SoundManager OEManager; -typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; // Set the position on a sound based on a Ptr. static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) @@ -60,175 +57,65 @@ static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) namespace MWSound { - struct SoundManager::SoundImpl - { - /* This is the sound manager. It loades, stores and deletes - sounds based on the sound factory it is given. - */ - OEManagerPtr mgr; - SoundPtr music; - /* This class calls update() on the sound manager each frame - using and Ogre::FrameListener - */ - Mangle::Sound::OgreOutputUpdater updater; - - /* This class tracks the movement of an Ogre::Camera and moves - a sound listener automatically to follow it. - */ - Mangle::Sound::OgreListenerMover cameraTracker; - - const ESMS::ESMStore &store; - - typedef std::map IDMap; - typedef std::map PtrMap; - PtrMap sounds; - - // This is used for case insensitive and slash-type agnostic file - // finding. It takes DOS paths (any case, \\ slashes or / slashes) - // relative to the sound dir, and translates them into full paths - // of existing files in the filesystem, if they exist. - bool FSstrict; - FileFinder::LessTreeFileFinder files; - FileFinder::StrictTreeFileFinder strict; - FileFinder::LessTreeFileFinder musicpath; - FileFinder::StrictTreeFileFinder musicpathStrict; - - SoundImpl(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &str, - const Files::PathContainer& soundDir, - const Files::PathContainer& musicDir, - bool fsstrict) - : mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) - , updater(mgr) - , cameraTracker(mgr) - , store(str) - , FSstrict(fsstrict) - , files(soundDir) - , strict(soundDir) - , musicpath(musicDir) - , musicpathStrict(musicDir) + SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, + const Files::PathContainer& dataDirs, + bool useSound, bool fsstrict, MWWorld::Environment& environment) + : mFSStrict(fsstrict) + , mEnvironment(environment) + , mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) + , updater(mgr) + , cameraTracker(mgr) + , mCurrentPlaylist(NULL) { + if(useSound) + { + // The music library will accept these filetypes + // If none is given then it will accept all filetypes + std::vector acceptableExtensions; + acceptableExtensions.push_back(".mp3"); + acceptableExtensions.push_back(".wav"); + acceptableExtensions.push_back(".ogg"); + acceptableExtensions.push_back(".flac"); - std::cout << "Sound output: " << SOUND_OUT << std::endl; - std::cout << "Sound decoder: " << SOUND_IN << std::endl; - // Attach the camera to the camera tracker - cameraTracker.followCamera(camera); + // Makes a list of all sound files, searches in reverse for priority reasons + for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it) + { + Files::FileLister(*it / std::string("Sound"), mSoundFiles, true); + } - // Tell Ogre to update the sound system each frame - root->addFrameListener(&updater); - } + // Makes a FileLibrary of all music files, searches in reverse for priority reasons + for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it) + { + mMusicLibrary.add(*it / std::string("Music"), true, mFSStrict, acceptableExtensions); + } - ~SoundImpl() + std::string anything = "anything"; // anything is better that a segfault + mCurrentPlaylist = mMusicLibrary.section(anything, mFSStrict); // now points to an empty path + + std::cout << "Sound output: " << SOUND_OUT << std::endl; + std::cout << "Sound decoder: " << SOUND_IN << std::endl; + // Attach the camera to the camera tracker + cameraTracker.followCamera(camera); + + // Tell Ogre to update the sound system each frame + root->addFrameListener(&updater); + } + } + + SoundManager::~SoundManager() { Ogre::Root::getSingleton().removeFrameListener(&updater); cameraTracker.unfollowCamera(); } - - - static std::string toMp3(std::string str) - { - std::string::size_type i = str.rfind('.'); - if(str.find('/', i) == std::string::npos && - str.find('\\', i) == std::string::npos) - str = str.substr(0, i) + ".mp3"; - else - str += ".mp3"; - return str; - } - - bool hasFile(const std::string &str, bool music = false) - { - bool found = false; - if(!FSstrict) - { - if(music) - { - found = musicpath.has(str); - // Not found? Try with .mp3 - if (!found) - { - found = musicpath.has(toMp3(str)); - } - } - else - { - found = files.has(str); - if (!found) - { - found = files.has(toMp3(str)); - } - } - } - else - { - if(music) - { - found = musicpathStrict.has(str); - // Not found? Try with .mp3 - if (!found) - { - found = musicpathStrict.has(toMp3(str)); - } - } - else - { - found = strict.has(str); - if (!found) - { - found = strict.has(toMp3(str)); - } - } - } - - return found; - } - - // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path - // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) - std::string convertPath(const std::string &str, bool music = false) - { - if(FSstrict == false) - { - // Search and return - if(music && musicpath.has(str)) - return musicpath.lookup(str); - else if(files.has(str)) - return files.lookup(str); - - // Try mp3 if the wav wasn't found - std::string mp3 = toMp3(str); - if(music && musicpath.has(mp3)) - return musicpath.lookup(mp3); - else if(files.has(mp3)) - return files.lookup(mp3); - } - else - { - if(music && musicpathStrict.has(str)) - return musicpathStrict.lookup(str); - else if(strict.has(str)) - return strict.lookup(str); - - // Try mp3 if the wav wasn't found - std::string mp3 = toMp3(str); - if(music && musicpathStrict.has(mp3)) - return musicpathStrict.lookup(mp3); - else if(strict.has(str)) - return strict.lookup(mp3); - } - - // Give up - return ""; - } - // Convert a soundId to file name, and modify the volume // according to the sounds local volume setting, minRange and // maxRange. - std::string lookup(const std::string &soundId, + std::string SoundManager::lookup(const std::string &soundId, float &volume, float &min, float &max) { - const ESM::Sound *snd = store.sounds.search(soundId); + const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId); if(snd == NULL) return ""; if(snd->data.volume == 0) @@ -249,16 +136,16 @@ namespace MWSound max = std::max(min, max); } - return convertPath(snd->sound); + return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict, false); } // Add a sound to the list and play it - void add(const std::string &file, + void SoundManager::add(const std::string &file, MWWorld::Ptr ptr, const std::string &id, float volume, float pitch, float min, float max, - bool loop) + bool loop, bool untracked) { try { @@ -270,7 +157,10 @@ namespace MWSound setPos(snd, ptr); snd->play(); - sounds[ptr][id] = WSoundPtr(snd); + if (!untracked) + { + sounds[ptr][id] = WSoundPtr(snd); + } } catch(...) { @@ -280,7 +170,7 @@ namespace MWSound // Clears all the sub-elements of a given iterator, and then // removes it from 'sounds'. - void clearAll(PtrMap::iterator& it) + void SoundManager::clearAll(PtrMap::iterator& it) { IDMap::iterator sit = it->second.begin(); @@ -301,7 +191,7 @@ namespace MWSound // Stop a sound and remove it from the list. If id="" then // remove the entire object and stop all its sounds. - void remove(MWWorld::Ptr ptr, const std::string &id = "") + void SoundManager::remove(MWWorld::Ptr ptr, const std::string &id) { PtrMap::iterator it = sounds.find(ptr); if(it != sounds.end()) @@ -324,7 +214,7 @@ namespace MWSound } } - bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const + bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const { PtrMap::const_iterator it = sounds.find(ptr); if(it != sounds.end()) @@ -348,7 +238,7 @@ namespace MWSound } // Remove all references to objects belonging to a given cell - void removeCell(const MWWorld::Ptr::CellStore *cell) + void SoundManager::removeCell(const MWWorld::Ptr::CellStore *cell) { PtrMap::iterator it2, it = sounds.begin(); while(it != sounds.end()) @@ -360,7 +250,7 @@ namespace MWSound } } - void updatePositions(MWWorld::Ptr ptr) + void SoundManager::updatePositions(MWWorld::Ptr ptr) { // Find the reference (if any) PtrMap::iterator it = sounds.find(ptr); @@ -378,85 +268,45 @@ namespace MWSound } } } - }; /* SoundImpl */ + + void SoundManager::stopMusic() + { + if (music) + music->stop(); + setPlaylist(); + } + void SoundManager::streamMusicFull(const std::string& filename) { - if(!mData) return; - // Play the sound and tell it to stream, if possible. TODO: // Store the reference, the jukebox will need to check status, // control volume etc. - if (mData->music) - mData->music->stop(); - mData->music = mData->mgr->load(filename); - mData->music->setStreaming(true); - mData->music->setVolume(0.4); - mData->music->play(); + if (music) + music->stop(); + music = mgr->load(filename); + music->setStreaming(true); + music->setVolume(0.4); + music->play(); } - SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, - const ESMS::ESMStore &store, const Files::PathContainer& dataDirs, - bool useSound, bool fsstrict, MWWorld::Environment& environment) - : mData(NULL) - , fsStrict(fsstrict) - , mEnvironment(environment) - { - for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) - { - MP3Lookup((*it) / "Music/Explore/"); - } - - if(useSound) - { - Files::PathContainer soundDirs;; - for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) - { - soundDirs.push_back( *it / std::string("Sound")); - } - mData = new SoundImpl(root, camera, store, soundDirs /* Sound */, dataDirs /* Music */, fsstrict); - } - - test.name = ""; - total = 0; - } - - SoundManager::~SoundManager() - { - if(mData) - delete mData; - } - void SoundManager::streamMusic(const std::string& filename) { - if(mData->hasFile(filename, true)) + std::string filePath = mMusicLibrary.locate(filename, mFSStrict, true).string(); + if(!filePath.empty()) { - streamMusicFull(mData->convertPath(filename, true)); + streamMusicFull(filePath); } } - void SoundManager::MP3Lookup(const boost::filesystem::path& dir) - { - boost::filesystem::directory_iterator dir_iter(dir), dir_end; - - std::string mp3extension = ".mp3"; - for(;dir_iter != dir_end; dir_iter++) - { - if(boost::filesystem::extension(*dir_iter) == mp3extension) - { - files.push_back(*dir_iter); - } - } - } - void SoundManager::startRandomTitle() { - if(!files.empty()) + if(mCurrentPlaylist && !mCurrentPlaylist->empty()) { - Files::PathContainer::iterator fileIter = files.begin(); + Files::PathContainer::const_iterator fileIter = mCurrentPlaylist->begin(); srand( time(NULL) ); - int r = rand() % files.size() + 1; //old random code + int r = rand() % mCurrentPlaylist->size() + 1; //old random code std::advance(fileIter, r - 1); std::string music = fileIter->string(); @@ -476,105 +326,158 @@ namespace MWSound bool SoundManager::isMusicPlaying() { bool test = false; - if(mData && mData->music) + if(music) { - test = mData->music->isPlaying(); + test = music->isPlaying(); } return test; } - SoundManager::SoundImpl SoundManager::getMData() - { - // bool test = mData->music->isPlaying(); - return *mData; - } + bool SoundManager::setPlaylist(std::string playlist) + { + const Files::PathContainer* previousPlaylist; + previousPlaylist = mCurrentPlaylist; + if (playlist == "") + { + mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict); + } + else if(mMusicLibrary.containsSection(playlist, mFSStrict)) + { + mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict); + } + else + { + std::cout << "Warning: playlist named " << playlist << " does not exist.\n"; + } + return previousPlaylist == mCurrentPlaylist; + } + + void SoundManager::playPlaylist(std::string playlist) + { + if (playlist == "") + { + if(!isMusicPlaying()) + { + startRandomTitle(); + } + return; + } + + if(!setPlaylist(playlist)) + { + startRandomTitle(); + } + else if (!isMusicPlaying()) + { + startRandomTitle(); + } + } void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename) { // The range values are not tested - if(!mData) return; - if(mData->hasFile(filename)) - mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 20000, false); + std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict, true); + if(!filePath.empty()) + add(filePath, ptr, "_say_sound", 1, 1, 100, 20000, false); else std::cout << "Sound file " << filename << " not found, skipping.\n"; } bool SoundManager::sayDone (MWWorld::Ptr ptr) const { - if(!mData) return false; - return !mData->isPlaying(ptr, "_say_sound"); + return !isPlaying(ptr, "_say_sound"); } - void SoundManager::playSound(const std::string& soundId, float volume, float pitch) + void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop) { - if(!mData) return; - // Play and forget float min, max; - const std::string &file = mData->lookup(soundId, volume, min, max); + const std::string &file = lookup(soundId, volume, min, max); if (file != "") { - SoundPtr snd = mData->mgr->load(file); + SoundPtr snd = mgr->load(file); + snd->setRepeat(loop); snd->setVolume(volume); snd->setRange(min,max); snd->setPitch(pitch); + snd->setRelative(true); snd->play(); + + if (loop) + { + // Only add the looping sound once + IDMap::iterator it = mLoopedSounds.find(soundId); + if(it == mLoopedSounds.end()) + { + mLoopedSounds[soundId] = WSoundPtr(snd); + } + } } } void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId, - float volume, float pitch, bool loop) + float volume, float pitch, bool loop, bool untracked) { - if(!mData) return; - // Look up the sound in the ESM data float min, max; - const std::string &file = mData->lookup(soundId, volume, min, max); + const std::string &file = lookup(soundId, volume, min, max); if (file != "") - mData->add(file, ptr, soundId, volume, pitch, min, max, loop); + add(file, ptr, soundId, volume, pitch, min, max, loop, untracked); } void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId) { - if(!mData) return; - mData->remove(ptr, soundId); + remove(ptr, soundId); } void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell) { - if(!mData) return; - mData->removeCell(cell); + removeCell(cell); } + void SoundManager::stopSound(const std::string& soundId) + { + IDMap::iterator it = mLoopedSounds.find(soundId); + if(it != mLoopedSounds.end()) + { + SoundPtr snd = it->second.lock(); + if(snd) snd->stop(); + mLoopedSounds.erase(it); + } + } + bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const { // Mark all sounds as playing, otherwise the scripts will just // keep trying to play them every frame. - if(!mData) return true; - return mData->isPlaying(ptr, soundId); + return isPlaying(ptr, soundId); } void SoundManager::updateObject(MWWorld::Ptr ptr) { - if (mData != NULL) - { - mData->updatePositions(ptr); - } + updatePositions(ptr); } void SoundManager::update (float duration) { MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell(); + static int total = 0; + static std::string regionName = ""; + static float timePassed = 0.0; + timePassed += duration; //If the region has changed - if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10) + if(!(current->cell->data.flags & current->cell->Interior) && timePassed >= 10) { - timer.restart(); - if (test.name != current->cell->region) + + ESM::Region test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region)); + + timePassed = 0; + if (regionName != current->cell->region) { + regionName = current->cell->region; total = 0; - test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region)); } if(test.soundList.size() > 0) @@ -598,15 +501,15 @@ namespace MWSound soundIter = test.soundList.begin(); while (soundIter != test.soundList.end()) { - const ESM::NAME32 go = soundIter->sound; + const std::string go = soundIter->sound.toString(); int chance = (int) soundIter->chance; //std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; soundIter++; if( r - pos < chance) { //play sound - std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; - mEnvironment.mSoundManager->playSound(go.name, 20.0, 1.0); + std::cout << "Sound: " << go <<" Chance:" << chance << "\n"; + mEnvironment.mSoundManager->playSound(go, 20.0, 1.0); break; } @@ -616,7 +519,7 @@ namespace MWSound } else if(current->cell->data.flags & current->cell->Interior) { - test.name = ""; + regionName = ""; } } diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 5c3f473f2..bd3b67679 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -3,13 +3,14 @@ #include -#include -#include +#include +#include + +#include + +#include #include "../mwworld/ptr.hpp" -#include -#include - namespace Ogre @@ -18,11 +19,16 @@ namespace Ogre class Camera; } -namespace ESMS +namespace Mangle { - struct ESMStore; + namespace Sound + { + typedef boost::shared_ptr SoundPtr; + } } +typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; + namespace MWWorld { struct Environment; @@ -30,43 +36,94 @@ namespace MWWorld namespace MWSound { - //SoundPtr *music; class SoundManager { - // Hide implementation details - engine.cpp is compiling - // enough as it is. - struct SoundImpl; - SoundImpl *mData; - Files::PathContainer files; - bool fsStrict; + // This is used for case insensitive and slash-type agnostic file + // finding. It takes DOS paths (any case, \\ slashes or / slashes) + // relative to the sound dir, and translates them into full paths + // of existing files in the filesystem, if they exist. + bool mFSStrict; + MWWorld::Environment& mEnvironment; - int total; - ESM::Region test; - boost::timer timer; - void streamMusicFull (const std::string& filename); ///< Play a soundifle /// \param absolute filename + /* This is the sound manager. It loades, stores and deletes + sounds based on the sound factory it is given. + */ + OEManagerPtr mgr; + Mangle::Sound::SoundPtr music; + + /* This class calls update() on the sound manager each frame + using and Ogre::FrameListener + */ + Mangle::Sound::OgreOutputUpdater updater; + + /* This class tracks the movement of an Ogre::Camera and moves + a sound listener automatically to follow it. + */ + Mangle::Sound::OgreListenerMover cameraTracker; + + typedef std::map IDMap; + typedef std::map PtrMap; + PtrMap sounds; + + // A list of all sound files used to lookup paths + Files::PathContainer mSoundFiles; + + // A library of all Music file paths stored by the folder they are contained in + Files::FileLibrary mMusicLibrary; + + // Points to the current playlist of music files stored in the music library + const Files::PathContainer* mCurrentPlaylist; + + IDMap mLoopedSounds; + + std::string lookup(const std::string &soundId, + float &volume, float &min, float &max); + void add(const std::string &file, + MWWorld::Ptr ptr, const std::string &id, + float volume, float pitch, float min, float max, + bool loop, bool untracked=false); + void clearAll(PtrMap::iterator& it); + void remove(MWWorld::Ptr ptr, const std::string &id = ""); + bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const; + void removeCell(const MWWorld::Ptr::CellStore *cell); + void updatePositions(MWWorld::Ptr ptr); + public: - SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, + SoundManager(Ogre::Root*, Ogre::Camera*, const Files::PathContainer& dataDir, bool useSound, bool fsstrict, MWWorld::Environment& environment); ~SoundManager(); + void stopMusic(); + ///< Stops music if it's playing + void streamMusic(const std::string& filename); ///< Play a soundifle /// \param filename name of a sound file in "Music/" in the data directory. void startRandomTitle(); - void MP3Lookup(const boost::filesystem::path& dir); + ///< Starts a random track from the current playlist bool isMusicPlaying(); + ///< Returns true if music is playing - SoundImpl getMData(); + bool setPlaylist(std::string playlist=""); + ///< Set the playlist to an existing folder + /// \param name of the folder that contains the playlist + /// if none is set then it is set to an empty playlist + /// \return Return true if the previous playlist was the same + + void playPlaylist(std::string playlist=""); + ///< Start playing music from the selected folder + /// \param name of the folder that contains the playlist + /// if none is set then it plays from the current playlist void say (MWWorld::Ptr reference, const std::string& filename); ///< Make an actor say some text. @@ -75,11 +132,11 @@ namespace MWSound bool sayDone (MWWorld::Ptr reference) const; ///< Is actor not speaking? - void playSound (const std::string& soundId, float volume, float pitch); + void playSound (const std::string& soundId, float volume, float pitch, bool loop=false); ///< Play a sound, independently of 3D-position void playSound3D (MWWorld::Ptr reference, const std::string& soundId, - float volume, float pitch, bool loop); + float volume, float pitch, bool loop, bool untracked=false); ///< Play a sound from an object void stopSound3D (MWWorld::Ptr reference, const std::string& soundId = ""); @@ -89,6 +146,9 @@ namespace MWSound void stopSound (MWWorld::Ptr::CellStore *cell); ///< Stop all sounds for the given cell. + void stopSound(const std::string& soundId); + ///< Stop a non-3d looping sound + bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 079c888aa..8c657a5c8 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -5,6 +5,8 @@ #include #include "world.hpp" +#include "class.hpp" +#include "containerstore.hpp" MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { @@ -35,6 +37,39 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) } } +void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) +{ + for (ESMS::CellRefList::List::iterator iter ( + cellStore.containers.list.begin()); + iter!=cellStore.containers.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } + + for (ESMS::CellRefList::List::iterator iter ( + cellStore.creatures.list.begin()); + iter!=cellStore.creatures.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } + + for (ESMS::CellRefList::List::iterator iter ( + cellStore.npcs.list.begin()); + iter!=cellStore.npcs.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } +} + MWWorld::Cells::Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world) : mStore (store), mReader (reader), mWorld (world) {} @@ -43,6 +78,8 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) std::map, Ptr::CellStore>::iterator result = mExteriors.find (std::make_pair (x, y)); + bool fill = false; + if (result==mExteriors.end()) { const ESM::Cell *cell = mStore.cells.searchExt (x, y); @@ -63,11 +100,16 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) result = mExteriors.insert (std::make_pair ( std::make_pair (x, y), Ptr::CellStore (cell))).first; + + fill = true; } if (result->second.mState!=Ptr::CellStore::State_Loaded) result->second.load (mStore, mReader); + if (fill) + fillContainers (result->second); + return &result->second; } @@ -75,16 +117,23 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) { std::map::iterator result = mInteriors.find (name); + bool fill = false; + if (result==mInteriors.end()) { const ESM::Cell *cell = mStore.cells.findInt (name); result = mInteriors.insert (std::make_pair (name, Ptr::CellStore (cell))).first; + + fill = true; } if (result->second.mState!=Ptr::CellStore::State_Loaded) result->second.load (mStore, mReader); + if (fill) + fillContainers (result->second); + return &result->second; } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0ecbd02d3..42aa1050c 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -34,6 +34,8 @@ namespace MWWorld Ptr::CellStore *getCellStore (const ESM::Cell *cell); + void fillContainers (Ptr::CellStore& cellStore); + public: Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 641da73e1..9d766909f 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -77,6 +77,11 @@ namespace MWWorld throw std::runtime_error ("class does not have a container store"); } + InventoryStore& Class::getInventoryStore (const Ptr& ptr) const + { + throw std::runtime_error ("class does not have an inventory store"); + } + void Class::lock (const Ptr& ptr, int lockLevel) const { throw std::runtime_error ("class does not support locking"); @@ -122,6 +127,16 @@ namespace MWWorld return Ogre::Vector3 (0, 0, 0); } + std::pair, bool> Class::getEquipmentSlots (const Ptr& ptr) const + { + return std::make_pair (std::vector(), false); + } + + int Class::getEquipmentSkill (const Ptr& ptr, const Environment& environment) const + { + return -1; + } + const Class& Class::get (const std::string& key) { std::map >::const_iterator iter = sClasses.find (key); @@ -141,4 +156,14 @@ namespace MWWorld { sClasses.insert (std::make_pair (key, instance)); } + + std::string Class::getUpSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const + { + throw std::runtime_error ("class does not have an up sound"); + } + + std::string Class::getDownSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const + { + throw std::runtime_error ("class does not have an down sound"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 9b6acb3ce..67320b3e0 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -34,6 +35,7 @@ namespace MWWorld class Ptr; class Environment; class ContainerStore; + class InventoryStore; /// \brief Base class for referenceable esm records class Class @@ -108,6 +110,10 @@ namespace MWWorld ///< Return container store or throw an exception, if class does not have a /// container store (default implementation: throw an exceoption) + virtual InventoryStore& getInventoryStore (const Ptr& ptr) const; + ///< Return inventory store or throw an exception, if class does not have a + /// inventory store (default implementation: throw an exceoption) + virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) @@ -137,6 +143,18 @@ namespace MWWorld ///< Return desired movement vector (determined based on movement settings, /// stance and stats). + virtual std::pair, bool> getEquipmentSlots (const Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + /// + /// Default implementation: return (empty vector, false). + + virtual int getEquipmentSkill (const Ptr& ptr, const Environment& environment) + const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + /// (default implementation: return -1) + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. @@ -144,6 +162,14 @@ namespace MWWorld ///< If there is no class for this pointer, an exception is thrown. static void registerClass (const std::string& key, boost::shared_ptr instance); + + virtual std::string getUpSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the up sound ID of \a ptr or throw an exception, if class does not support ID retrieval + /// (default implementation: throw an exception) + + virtual std::string getDownSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval + /// (default implementation: throw an exception) }; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 54908deec..c498cabce 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -5,6 +5,12 @@ #include #include +#include + +#include "manualref.hpp" + +MWWorld::ContainerStore::~ContainerStore() {} + MWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask) { return ContainerStoreIterator (mask, this); @@ -17,7 +23,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() void MWWorld::ContainerStore::add (const Ptr& ptr) { - /// \todo implement item stocking + /// \todo implement item stacking switch (getType (ptr)) { @@ -36,6 +42,40 @@ void MWWorld::ContainerStore::add (const Ptr& ptr) } } +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const ESMS::ESMStore& store) +{ + for (std::vector::const_iterator iter (items.list.begin()); iter!=items.list.end(); + ++iter) + { + ManualRef ref (store, iter->item.toString()); + + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + { + /// \todo implement leveled item lists + continue; + } + + ref.getPtr().getRefData().setCount (iter->count); + add (ref.getPtr()); + } +} + +void MWWorld::ContainerStore::clear() +{ + potions.list.clear(); + appas.list.clear(); + armors.list.clear(); + books.list.clear(); + clothes.list.clear(); + ingreds.list.clear(); + lights.list.clear(); + lockpicks.list.clear(); + miscItems.list.clear(); + probes.list.clear(); + repairs.list.clear(); + weapons.list.clear(); +} + int MWWorld::ContainerStore::getType (const Ptr& ptr) { if (ptr.isEmpty()) @@ -331,6 +371,11 @@ int MWWorld::ContainerStoreIterator::getType() const return mType; } +const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStore() const +{ + return mContainer; +} + bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) { return left.isEqual (right); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 7263245f3..69e5ba446 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -1,11 +1,18 @@ #ifndef GAME_MWWORLD_CONTAINERSTORE_H #define GAME_MWWORLD_CONTAINERSTORE_H +#include + #include #include "refdata.hpp" #include "ptr.hpp" +namespace ESM +{ + struct InventoryList; +} + namespace MWWorld { class ContainerStoreIterator; @@ -48,6 +55,8 @@ namespace MWWorld public: + virtual ~ContainerStore(); + ContainerStoreIterator begin (int mask = Type_All); ContainerStoreIterator end(); @@ -60,6 +69,12 @@ namespace MWWorld /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! + void fill (const ESM::InventoryList& items, const ESMS::ESMStore& store); + ///< Insert items into *this. + + void clear(); + ///< Empty container. + static int getType (const Ptr& ptr); ///< This function throws an exception, if ptr does not point to an object, that can be /// put into a container. @@ -71,6 +86,7 @@ namespace MWWorld /// /// \note The iterator will automatically skip over deleted objects. class ContainerStoreIterator + : public std::iterator { int mType; int mMask; @@ -126,6 +142,8 @@ namespace MWWorld int getType() const; + const ContainerStore *getContainerStore() const; + friend class ContainerStore; }; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp new file mode 100644 index 000000000..264be7bb3 --- /dev/null +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -0,0 +1,86 @@ + +#include "inventorystore.hpp" + +#include +#include + +#include "class.hpp" + +void MWWorld::InventoryStore::copySlots (const InventoryStore& store) +{ + // some const-trickery, required because of a flaw in the handling of MW-references and the + // resulting workarounds + for (std::vector::const_iterator iter ( + const_cast (store).mSlots.begin()); + iter!=const_cast (store).mSlots.end(); ++iter) + { + std::size_t distance = std::distance (const_cast (store).begin(), *iter); + + ContainerStoreIterator slot = begin(); + + std::advance (slot, distance); + + mSlots.push_back (slot); + } +} + +MWWorld::InventoryStore::InventoryStore() +{ + for (int i=0; i=static_cast (mSlots.size())) + throw std::runtime_error ("slot number out of range"); + + if (iterator.getContainerStore()!=this) + throw std::runtime_error ("attempt to equip an item that is not in the inventory"); + + if (iterator!=end()) + { + std::pair, bool> slots = Class::get (*iterator).getEquipmentSlots (*iterator); + + if (std::find (slots.first.begin(), slots.first.end(), slot)==slots.first.end()) + throw std::runtime_error ("invalid slot"); + } + + /// \todo restack item previously in this slot (if required) + + /// \todo unstack item pointed to by iterator if required) + + mSlots[slot] = iterator; +} + +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) +{ + if (slot<0 || slot>=static_cast (mSlots.size())) + throw std::runtime_error ("slot number out of range"); + + if (mSlots[slot]==end()) + return end(); + + if (mSlots[slot]->getRefData().getCount()<1) + { + // object has been deleted + mSlots[slot] = end(); + return end(); + } + + return mSlots[slot]; +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp new file mode 100644 index 000000000..c41f9e8e3 --- /dev/null +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -0,0 +1,58 @@ +#ifndef GAME_MWWORLD_INVENTORYSTORE_H +#define GAME_MWWORLD_INVENTORYSTORE_H + +#include "containerstore.hpp" + +namespace MWWorld +{ + ///< \brief Variant of the ContainerStore for NPCs + class InventoryStore : public ContainerStore + { + public: + + static const int Slot_Helmet = 0; + static const int Slot_Cuirass = 1; + static const int Slot_Greaves = 2; + static const int Slot_LeftPauldron = 3; + static const int Slot_RightPauldron = 4; + static const int Slot_LeftGauntlet = 5; + static const int Slot_RightGauntlet = 6; + static const int Slot_Boots = 7; + static const int Slot_Shirt = 8; + static const int Slot_Pants = 9; + static const int Slot_Skirt = 10; + static const int Slot_Robe = 11; + static const int Slot_LeftRing = 12; + static const int Slot_RightRing = 13; + static const int Slot_Amulet = 14; + static const int Slot_Belt = 15; + static const int Slot_CarriedRight = 16; + static const int Slot_CarriedLeft = 17; + static const int Slot_Ammunition = 18; + + static const int Slots = 19; + + static const int Slot_NoSlot = -1; + + private: + + mutable std::vector mSlots; + + void copySlots (const InventoryStore& store); + + public: + + InventoryStore(); + + InventoryStore (const InventoryStore& store); + + InventoryStore& operator= (const InventoryStore& store); + + void equip (int slot, const ContainerStoreIterator& iterator); + ///< \note \a iteartor can be an end-iterator + + ContainerStoreIterator getSlot (int slot); + }; +} + +#endif diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 90afc4e78..db25c1d27 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -496,7 +496,10 @@ WeatherResult WeatherManager::transition(float factor) void WeatherManager::update(float duration) { mWeatherUpdateTime -= duration; - if (mEnvironment->mWorld->isCellExterior() || mEnvironment->mWorld->isCellQuasiExterior()) + + bool exterior = (mEnvironment->mWorld->isCellExterior() || mEnvironment->mWorld->isCellQuasiExterior()); + + if (exterior) { std::string regionstr = mEnvironment->mWorld->getPlayer().getPlayer().getCell()->cell->region; boost::algorithm::to_lower(regionstr); @@ -671,7 +674,7 @@ void WeatherManager::update(float duration) mRendering->getSkyManager()->secundaDisable(); } - if (mCurrentWeather == "thunderstorm" && mNextWeather == "") + if (mCurrentWeather == "thunderstorm" && mNextWeather == "" && exterior) { if (mThunderFlash > 0) { @@ -730,6 +733,42 @@ void WeatherManager::update(float duration) mRendering->skyDisable(); mRendering->getSkyManager()->setThunder(0.f); } + + // play sounds + std::string ambientSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID : ""); + if (!exterior) ambientSnd = ""; + if (ambientSnd != "") + { + if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) + { + mSoundsPlaying.push_back(ambientSnd); + mEnvironment->mSoundManager->playSound(ambientSnd, 1.0, 1.0, true); + } + } + + std::string rainSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mRainLoopSoundID : ""); + if (!exterior) rainSnd = ""; + if (rainSnd != "") + { + if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) + { + mSoundsPlaying.push_back(rainSnd); + mEnvironment->mSoundManager->playSound(rainSnd, 1.0, 1.0, true); + } + } + + // stop sounds + std::vector::iterator it=mSoundsPlaying.begin(); + while (it!=mSoundsPlaying.end()) + { + if ( *it != ambientSnd && *it != rainSnd) + { + mEnvironment->mSoundManager->stopSound(*it); + it = mSoundsPlaying.erase(it); + } + else + ++it; + } } void WeatherManager::setHour(const float hour) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 9353f7cd1..b9b40e6fa 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -243,8 +243,10 @@ namespace MWWorld MWWorld::Environment* mEnvironment; std::map mWeatherSettings; - + std::map mRegionOverrides; + + std::vector mSoundsPlaying; Ogre::String mCurrentWeather; Ogre::String mNextWeather; diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 1a69f4d7b..1c47e3c71 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -161,9 +161,9 @@ namespace MWWorld { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); - + mRendering = new MWRender::RenderingManager(renderer, resDir, mPhysEngine, environment); - + mWeatherManager = new MWWorld::WeatherManager(mRendering, &environment); boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master)); @@ -381,7 +381,7 @@ namespace MWWorld mGlobalVariables->setFloat ("gamehour", hour); mRendering->skySetHour (hour); - + mWeatherManager->setHour (hour); if (days>0) @@ -418,10 +418,10 @@ namespace MWWorld mGlobalVariables->setInt ("month", month); mRendering->skySetDate (day, month); - + mWeatherManager->setDate (day, month); - - + + } void World::setMonth (int month) @@ -702,9 +702,9 @@ namespace MWWorld void World::update (float duration) { mWorldScene->update (duration); - + mWeatherManager->update (duration); - + // cast a ray from player to sun to detect if the sun is visible // this is temporary until we find a better place to put this code // currently its here because we need to access the physics system @@ -713,7 +713,7 @@ namespace MWWorld sun = Vector3(sun.x, -sun.z, sun.y); mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); } - + bool World::isCellExterior() const { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); @@ -726,7 +726,7 @@ namespace MWWorld } return false; } - + bool World::isCellQuasiExterior() const { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); @@ -739,17 +739,17 @@ namespace MWWorld } return false; } - + int World::getCurrentWeather() const { return mWeatherManager->getWeatherID(); } - + void World::changeWeather(const std::string& region, const unsigned int id) { mWeatherManager->changeWeather(region, id); } - + OEngine::Render::Fader* World::getFader() { return mRendering->getFader(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6bf7bacf4..c95efb37d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -44,6 +44,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager + filelibrary ) add_component_dir (compiler diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index c255154b5..5d74ee9d4 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -95,8 +95,6 @@ namespace Compiler return true; } - - return false; } bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner) @@ -108,7 +106,7 @@ namespace Compiler Codes expr; mExprParser.append (expr); - Generator::jump (loop, -mCodeBlock.size()-expr.size()); + Generator::jump (loop, -static_cast (mCodeBlock.size()-expr.size())); std::copy (expr.begin(), expr.end(), std::back_inserter (mCode)); @@ -122,7 +120,7 @@ namespace Compiler Codes loop2; - Generator::jump (loop2, -mCodeBlock.size()-expr.size()-skip.size()); + Generator::jump (loop2, -static_cast (mCodeBlock.size()-expr.size()-skip.size())); if (loop.size()!=loop2.size()) throw std::logic_error ( @@ -153,8 +151,6 @@ namespace Compiler return true; } - - return false; } ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index b0727b0c0..dc63ce335 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -3,32 +3,68 @@ namespace ESM { -void PathGrid::load(ESMReader &esm) +void Pathgrid::load(ESMReader &esm) { esm.getHNT(data, "DATA", 12); cell = esm.getHNString("NAME"); - // Remember this file position - context = esm.getContext(); + // keep track of total connections so we can reserve edge vector size + int edgeCount = 0; - // Check that the sizes match up. Size = 16 * s2 (path points?) if (esm.isNextSub("PGRP")) { - esm.skipHSub(); + esm.getSubHeader(); int size = esm.getSubSize(); - if (size != 16 * data.s2) - esm.fail("Path grid table size mismatch"); + // Check that the sizes match up. Size = 16 * s2 (path points) + if (size != static_cast (sizeof(Point) * data.s2)) + esm.fail("Path point subrecord size mismatch"); + else + { + int pointCount = data.s2; + points.reserve(pointCount); + for (int i = 0; i < pointCount; ++i) + { + Point p; + esm.getExact(&p, sizeof(Point)); + points.push_back(p); + edgeCount += p.connectionNum; + } + } } - // Size varies. Path grid chances? Connections? Multiples of 4 - // suggest either int or two shorts, or perhaps a float. Study - // it later. if (esm.isNextSub("PGRC")) { - esm.skipHSub(); + esm.getSubHeader(); int size = esm.getSubSize(); - if (size % 4 != 0) + if (size % sizeof(int) != 0) esm.fail("PGRC size not a multiple of 4"); + else + { + int rawConnNum = size / sizeof(int); + std::vector rawConnections; + rawConnections.reserve(rawConnNum); + for (int i = 0; i < rawConnNum; ++i) + { + int currentValue; + esm.getT(currentValue); + rawConnections.push_back(currentValue); + } + + std::vector::const_iterator rawIt = rawConnections.begin(); + int pointIndex = 0; + edges.reserve(edgeCount); + for(PointList::const_iterator it = points.begin(); it != points.end(); it++, pointIndex++) + { + unsigned char connectionNum = (*it).connectionNum; + for (int i = 0; i < connectionNum; ++i) { + Edge edge; + edge.v0 = pointIndex; + edge.v1 = *rawIt; + rawIt++; + edges.push_back(edge); + } + } + } } } diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index 8c030d314..6e2c6e134 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -9,20 +9,37 @@ namespace ESM /* * Path grid. */ -struct PathGrid +struct Pathgrid { struct DATAstruct { int x, y; // Grid location, matches cell for exterior cells short s1; // ?? Usually but not always a power of 2. Doesn't seem // to have any relation to the size of PGRC. - short s2; // Number of path points? Size of PGRP block is always 16 * s2; + short s2; // Number of path points. }; // 12 bytes + struct Point // path grid point + { + int x, y, z; // Location of point + unsigned char autogenerated; // autogenerated vs. user coloring flag? + unsigned char connectionNum; // number of connections for this point + short unknown; + }; // 16 bytes + + struct Edge // path grid edge + { + int v0, v1; // index of points connected with this edge + }; // 8 bytes + std::string cell; // Cell name DATAstruct data; - ESM_Context context; // Context so we can return here later and - // finish the job + + typedef std::vector PointList; + PointList points; + + typedef std::vector EdgeList; + EdgeList edges; void load(ESMReader &esm); }; diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 20a2e8ff9..678f794c8 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -22,6 +22,8 @@ namespace ESMS struct RecList { + virtual ~RecList() {} + virtual void load(ESMReader &esm, const std::string &id) = 0; virtual int getSize() = 0; virtual void listIdentifier (std::vector& identifier) const = 0; @@ -42,6 +44,8 @@ namespace ESMS template struct RecListT : RecList { + virtual ~RecListT() {} + typedef std::map MapType; MapType list; @@ -90,6 +94,8 @@ namespace ESMS template struct RecListWithIDT : RecList { + virtual ~RecListWithIDT() {} + typedef std::map MapType; MapType list; @@ -139,6 +145,8 @@ namespace ESMS template struct RecIDListT : RecList { + virtual ~RecIDListT() {} + typedef std::map MapType; MapType list; @@ -189,6 +197,8 @@ namespace ESMS */ struct LTexList : RecList { + virtual ~LTexList() {} + // TODO: For multiple ESM/ESP files we need one list per file. std::vector ltex; int count; @@ -223,6 +233,8 @@ namespace ESMS */ struct LandList : RecList { + virtual ~LandList() {} + // Map containing all landscapes typedef std::map LandsCol; typedef std::map Lands; @@ -296,7 +308,7 @@ namespace ESMS identifier.push_back (iter->first); } - ~CellList() + virtual ~CellList() { for (IntCells::iterator it = intCells.begin(); it!=intCells.end(); ++it) delete it->second; @@ -390,9 +402,100 @@ namespace ESMS } }; + struct PathgridList : RecList + { + int count; + + // List of grids for interior cells. Indexed by cell name. + typedef std::map IntGrids; + IntGrids intGrids; + + // List of grids for exterior cells. Indexed as extCells[gridX][gridY]. + typedef std::map, ESM::Pathgrid*> ExtGrids; + ExtGrids extGrids; + + PathgridList() : count(0) {} + + virtual ~PathgridList() + { + for (IntGrids::iterator it = intGrids.begin(); it!=intGrids.end(); ++it) + delete it->second; + + for (ExtGrids::iterator it = extGrids.begin(); it!=extGrids.end(); ++it) + delete it->second; + } + + int getSize() { return count; } + + virtual void listIdentifier (std::vector& identifier) const + { + // do nothing + } + + void load(ESMReader &esm, const std::string &id) + { + count++; + ESM::Pathgrid *grid = new ESM::Pathgrid; + grid->load(esm); + if (grid->data.x == 0 && grid->data.y == 0) + { + intGrids[grid->cell] = grid; + } + else + { + extGrids[std::make_pair(grid->data.x, grid->data.y)] = grid; + } + } + + Pathgrid *find(int cellX, int cellY, std::string cellName) const + { + Pathgrid *result = search(cellX, cellY, cellName); + if (!result) + { + throw std::runtime_error("no pathgrid found for cell " + cellName); + } + return result; + } + + Pathgrid *search(int cellX, int cellY, std::string cellName) const + { + Pathgrid *result = NULL; + if (cellX == 0 && cellY == 0) // possibly interior + { + IntGrids::const_iterator it = intGrids.find(cellName); + if (it != intGrids.end()) + result = it->second; + } + else + { + ExtGrids::const_iterator it = extGrids.find(std::make_pair(cellX, cellY)); + if (it != extGrids.end()) + result = it->second; + } + return result; + } + + Pathgrid *search(const ESM::Cell &cell) const + { + int cellX, cellY; + if (cell.data.flags & ESM::Cell::Interior) + { + cellX = cellY = 0; + } + else + { + cellX = cell.data.gridX; + cellY = cell.data.gridY; + } + return search(cellX, cellY, cell.name); + } + }; + template struct ScriptListT : RecList { + virtual ~ScriptListT() {} + typedef std::map MapType; MapType list; @@ -444,6 +547,8 @@ namespace ESMS template struct IndexListT { + virtual ~IndexListT() {} + typedef std::map MapType; MapType list; diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp index e3bbf9e82..fab04d3e9 100644 --- a/components/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -74,7 +74,8 @@ namespace ESMS ScriptListT