From b3735981d6fb5d9c040842c391c1643894aa7315 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 21:18:09 +0200 Subject: [PATCH 01/13] Remove unused file_finder --- apps/openmw_test_suite/CMakeLists.txt | 1 - .../file_finder/test_filefinder.cpp | 66 -------- .../components/file_finder/test_search.cpp | 74 --------- components/CMakeLists.txt | 4 - components/file_finder/file_finder.hpp | 142 ------------------ components/file_finder/filename_less.hpp | 84 ----------- components/file_finder/search.cpp | 36 ----- components/file_finder/search.hpp | 20 --- 8 files changed, 427 deletions(-) delete mode 100644 apps/openmw_test_suite/components/file_finder/test_filefinder.cpp delete mode 100644 apps/openmw_test_suite/components/file_finder/test_search.cpp delete mode 100644 components/file_finder/file_finder.hpp delete mode 100644 components/file_finder/filename_less.hpp delete mode 100644 components/file_finder/search.cpp delete mode 100644 components/file_finder/search.hpp diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 682010340..9fe7890ac 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -12,7 +12,6 @@ if (GTEST_FOUND AND GMOCK_FOUND) file(GLOB UNITTEST_SRC_FILES components/misc/test_*.cpp - components/file_finder/test_*.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp b/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp deleted file mode 100644 index 2d151988b..000000000 --- a/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include "components/file_finder/file_finder.hpp" - -struct FileFinderTest : public ::testing::Test -{ - protected: - FileFinderTest() - : mTestDir("./filefinder_test_dir/") - , mTestFile("test.txt") - , mTestFileUppercase("TEST.TXT") - , mTestFileNotFound("foobarbaz.txt") - { - } - - virtual void SetUp() - { - boost::filesystem::create_directory(boost::filesystem::path(mTestDir)); - - std::ofstream ofs(std::string(mTestDir + mTestFile).c_str(), std::ofstream::out); - ofs << std::endl; - ofs.close(); - } - - virtual void TearDown() - { - boost::filesystem::remove_all(boost::filesystem::path(mTestDir)); - } - - std::string mTestDir; - std::string mTestFile; - std::string mTestFileUppercase; - std::string mTestFileNotFound; -}; - -TEST_F(FileFinderTest, FileFinder_has_file) -{ - FileFinder::FileFinder fileFinder(mTestDir); - ASSERT_TRUE(fileFinder.has(mTestFile)); - ASSERT_TRUE(fileFinder.has(mTestFileUppercase)); - ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile)); - ASSERT_TRUE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile)); -} - -TEST_F(FileFinderTest, FileFinder_does_not_have_file) -{ - FileFinder::FileFinder fileFinder(mTestDir); - ASSERT_FALSE(fileFinder.has(mTestFileNotFound)); - ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty()); -} - -TEST_F(FileFinderTest, FileFinderStrict_has_file) -{ - FileFinder::FileFinderStrict fileFinder(mTestDir); - ASSERT_TRUE(fileFinder.has(mTestFile)); - ASSERT_FALSE(fileFinder.has(mTestFileUppercase)); - ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile)); - ASSERT_FALSE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile)); -} - -TEST_F(FileFinderTest, FileFinderStrict_does_not_have_file) -{ - FileFinder::FileFinderStrict fileFinder(mTestDir); - ASSERT_FALSE(fileFinder.has(mTestFileNotFound)); - ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty()); -} diff --git a/apps/openmw_test_suite/components/file_finder/test_search.cpp b/apps/openmw_test_suite/components/file_finder/test_search.cpp deleted file mode 100644 index 63745b625..000000000 --- a/apps/openmw_test_suite/components/file_finder/test_search.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include - -#include "components/file_finder/search.hpp" - -struct SearchTest : public ::testing::Test -{ - protected: - SearchTest() - : mTestDir("./search_test_dir/") - { - } - - virtual void SetUp() - { - boost::filesystem::create_directory(boost::filesystem::path(mTestDir)); - - std::ofstream ofs(std::string(mTestDir + "test2.txt").c_str(), std::ofstream::out); - ofs << std::endl; - ofs.close(); - } - - virtual void TearDown() - { - boost::filesystem::remove_all(boost::filesystem::path(mTestDir)); - } - - std::string mTestDir; -}; - -TEST_F(SearchTest, file_not_found) -{ - struct Result : public FileFinder::ReturnPath - { - Result(const boost::filesystem::path& expectedPath) - : mExpectedPath(expectedPath) - { - } - - void add(const boost::filesystem::path& p) - { - ASSERT_FALSE(p == mExpectedPath); - } - - private: - boost::filesystem::path mExpectedPath; - - } r(boost::filesystem::path(mTestDir + "test.txt")); - - FileFinder::find(mTestDir, r, false); -} - -TEST_F(SearchTest, file_found) -{ - struct Result : public FileFinder::ReturnPath - { - Result(const boost::filesystem::path& expectedPath) - : mExpectedPath(expectedPath) - { - } - - void add(const boost::filesystem::path& p) - { - ASSERT_TRUE(p == mExpectedPath); - } - - private: - boost::filesystem::path mExpectedPath; - - } r(boost::filesystem::path(mTestDir + "test2.txt")); - - FileFinder::find(mTestDir, r, false); -} diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d7bdaf36c..c7deeadf3 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -34,10 +34,6 @@ add_component_dir (to_utf8 to_utf8 ) -add_component_dir (file_finder - file_finder filename_less search - ) - add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst diff --git a/components/file_finder/file_finder.hpp b/components/file_finder/file_finder.hpp deleted file mode 100644 index 8a15af73a..000000000 --- a/components/file_finder/file_finder.hpp +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef FILE_FINDER_MAIN_H -#define FILE_FINDER_MAIN_H - -#include - -#include "search.hpp" -#include "filename_less.hpp" -#include - -namespace FileFinder -{ - -template -class FileFinderT -{ - typedef std::map TableContainer; - TableContainer table; - - struct Inserter : ReturnPath - { - FileFinderT *owner; - int cut; - - void add(const boost::filesystem::path &pth) - { - std::string file = pth.string(); - std::string key = file.substr(cut); - owner->table[key] = file; - } - }; - - Inserter inserter; - -public: - FileFinderT(const boost::filesystem::path &path, bool recurse=true) - { - inserter.owner = this; - - // Remember the original path length, so we can cut it away from - // the relative paths used as keys - const std::string& pstring = path.string(); - inserter.cut = pstring.size(); - - // If the path does not end in a slash, then boost will add one - // later, which means one more character we have to remove. - char last = *pstring.rbegin(); - if(last != '\\' && last != '/') - inserter.cut++; - - // Fill the map - find(path, inserter, recurse); - } - - bool has(const std::string& file) const - { - return table.find(file) != table.end(); - } - - // Find the full path from a relative path. - const std::string &lookup(const std::string& file) const - { - static std::string empty; - typename TableContainer::const_iterator it = table.find(file); - return (it != table.end()) ? it->second : empty; - } -}; - -template -< - class LESS -> -struct TreeFileFinder -{ - typedef TreeFileFinder finder_t; - - TreeFileFinder(const Files::PathContainer& paths, bool recurse = true) - { - struct : ReturnPath - { - finder_t *owner; - int cut; - - void add(const boost::filesystem::path &pth) - { - std::string file = pth.string(); - std::string key = file.substr(cut); - owner->mTable[key] = file; - } - } inserter; - - inserter.owner = this; - - for (Files::PathContainer::const_iterator it = paths.begin(); it != paths.end(); ++it) - { - - // Remember the original path length, so we can cut it away from - // the relative paths used as keys - const std::string& pstring = it->string(); - inserter.cut = pstring.size(); - - // If the path does not end in a slash, then boost will add one - // later, which means one more character we have to remove. - char last = *pstring.rbegin(); - if (last != '\\' && last != '/') - { - inserter.cut++; - } - - // Fill the map - find(*it, inserter, recurse); - } - } - - bool has(const std::string& file) const - { - return mTable.find(file) != mTable.end(); - } - - const std::string& lookup(const std::string& file) const - { - static std::string empty; - typename TableContainer::const_iterator it = mTable.find(file); - return (it != mTable.end()) ? it->second : empty; - } - - private: - typedef std::map TableContainer; - TableContainer mTable; - -// Inserter inserter; -}; - - -// The default is to use path_less for equality checks -typedef FileFinderT FileFinder; -typedef FileFinderT FileFinderStrict; - -typedef TreeFileFinder LessTreeFileFinder; -typedef TreeFileFinder StrictTreeFileFinder; - -} /* namespace FileFinder */ -#endif /* FILE_FINDER_MAIN_H */ diff --git a/components/file_finder/filename_less.hpp b/components/file_finder/filename_less.hpp deleted file mode 100644 index bc3186ce9..000000000 --- a/components/file_finder/filename_less.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef FILE_FINDER_LESS_H -#define FILE_FINDER_LESS_H - -#include -#include - -namespace FileFinder{ - -// Used for maps of file paths. Compares file paths, but ignores case -// AND treats \ and / as the same character. -struct path_less -{ - int compareChar(char a, char b) const - { - if(a>b) return 1; - else if(a= 'a' && a <= 'z') a += 'A'-'a'; - else if(a == '\\') a = '/'; - if(b >= 'a' && b <= 'z') b += 'A'-'a'; - else if(b == '\\') b = '/'; - return compareChar(a,b); - } - - int compareString(const char *a, const char *b) const - { - while(*a && *b) - { - int i = comparePathChar(*a,*b); - if(i != 0) return i; - a++; b++; - } - // At this point, one or both of the chars is a null terminator. - // Normal char comparison will get the correct final result here. - return compareChar(*a,*b); - } - - bool operator() (const std::string& a, const std::string& b) const - { - return compareString(a.c_str(), b.c_str()) < 0; - } -}; - -struct path_slash -{ - int compareChar(char a, char b) const - { - if(a>b) return 1; - else if(a - -void FileFinder::find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse) -{ - if (boost::filesystem::exists(dir_path)) - { - if (!recurse) - { - boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end - for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) - { - if (!boost::filesystem::is_directory( *itr )) - { - ret.add(*itr); - } - } - } - else - { - boost::filesystem::recursive_directory_iterator end_itr; // default construction yields past-the-end - for (boost::filesystem::recursive_directory_iterator itr(dir_path); itr != end_itr; ++itr) - { - if (!boost::filesystem::is_directory(*itr)) - { - ret.add(*itr); - } - } - } - } - else - { - std::cout << "Path " << dir_path << " not found" << std::endl; - } -} diff --git a/components/file_finder/search.hpp b/components/file_finder/search.hpp deleted file mode 100644 index 4e16fb64a..000000000 --- a/components/file_finder/search.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef FILE_FINDER_SEARCH_H -#define FILE_FINDER_SEARCH_H - -#include -#include - -namespace FileFinder -{ - struct ReturnPath - { - virtual void add(const boost::filesystem::path &pth) = 0; - }; - - /** Search the given path and return all file paths through 'ret'. If - recurse==true, all files in subdirectories are returned as well. - */ - void find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse=true); -} - -#endif From d70306382eac080677ef6bf3cea7303440a931ea Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 May 2014 00:58:12 +0200 Subject: [PATCH 02/13] Don't create the player object until the game starts Fixes a crash introduced in e591d23 when using --skip-menu=0. --- apps/openmw/engine.cpp | 44 +++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 03361408c..66c09b6ff 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -66,9 +66,13 @@ void OMW::Engine::executeLocalScripts() bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt) { - bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); - MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused); - MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused); + MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame); + } return true; } @@ -110,8 +114,12 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // update actors - MWBase::Environment::get().getMechanicsManager()->update(frametime, - paused); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + MWBase::Environment::get().getMechanicsManager()->update(frametime, + paused); + } if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) @@ -122,16 +130,24 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) } // update world - MWBase::Environment::get().getWorld()->update(frametime, paused); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + MWBase::Environment::get().getWorld()->update(frametime, paused); + } // update GUI - Ogre::RenderWindow* window = mOgre->getWindow(); - unsigned int tri, batch; - MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch); - MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + Ogre::RenderWindow* window = mOgre->getWindow(); + unsigned int tri, batch; + MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch); + MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); - MWBase::Environment::get().getWindowManager()->onFrame(frametime); - MWBase::Environment::get().getWindowManager()->update(); + MWBase::Environment::get().getWindowManager()->onFrame(frametime); + MWBase::Environment::get().getWindowManager()->update(); + } } catch (const std::exception& e) { @@ -393,10 +409,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage)); - mEnvironment.getWorld()->renderPlayer(); - mechanics->buildPlayer(); - window->updatePlayer(); - mOgre->getRoot()->addFrameListener (this); // scripts From 940a434479976e5dd5a48596804dbb4647a9265c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 May 2014 03:19:38 +0200 Subject: [PATCH 03/13] Use ItemModel for moving items from a container to the world Fixes owner not resetting when moving an item from a corpse to the world. --- apps/openmw/mwbase/world.hpp | 5 ++- apps/openmw/mwgui/container.cpp | 3 +- apps/openmw/mwgui/hud.cpp | 56 +++++++++++++++++++++++--------- apps/openmw/mwworld/worldimp.cpp | 9 ++--- apps/openmw/mwworld/worldimp.hpp | 5 ++- 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 0459d5341..e16f00186 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -354,15 +354,14 @@ namespace MWBase virtual void update (float duration, bool paused) = 0; - virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; + virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place - /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0; + virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0; ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 585647338..02c4b9356 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -91,7 +91,8 @@ namespace MWGui mSourceModel->update(); finish(); - targetView->update(); + if (targetView) + targetView->update(); // We need to update the view since an other item could be auto-equipped. mSourceView->update(); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index ccd4489ba..dc02f9976 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -17,9 +17,47 @@ #include "itemmodel.hpp" #include "container.hpp" +#include "itemmodel.hpp" + namespace MWGui { + /** + * Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world. + */ + class WorldItemModel : public ItemModel + { + public: + WorldItemModel(float left, float top) : mLeft(left), mTop(top) {} + virtual ~WorldItemModel() {} + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + + MWWorld::Ptr dropped; + if (world->canPlaceObject(mLeft, mTop)) + dropped = world->placeObject(item.mBase, mLeft, mTop, count); + else + dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count); + if (setNewOwner) + dropped.getCellRef().mOwner = ""; + + return dropped; + } + + virtual void removeItem (const ItemStack& item, size_t count) { throw std::runtime_error("removeItem not implemented"); } + virtual ModelIndex getIndex (ItemStack item) { throw std::runtime_error("getIndex not implemented"); } + virtual void update() {} + virtual size_t getItemCount() { return 0; } + virtual ItemStack getItem (ModelIndex index) { throw std::runtime_error("getItem not implemented"); } + + private: + // Where to drop the item + float mLeft; + float mTop; + }; + + HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) : Layout("openmw_hud.layout") , mHealth(NULL) @@ -229,10 +267,6 @@ namespace MWGui if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld - MWWorld::Ptr object = mDragAndDrop->mItem.mBase; - - MWBase::World* world = MWBase::Environment::get().getWorld(); - MWBase::Environment::get().getWorld()->breakInvisibility( MWBase::Environment::get().getWorld()->getPlayerPtr()); @@ -241,20 +275,10 @@ namespace MWGui float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY, mDragAndDrop->mDraggedCount); - else - world->dropObjectOnGround(world->getPlayerPtr(), object, mDragAndDrop->mDraggedCount); + WorldItemModel drop (mouseX, mouseY); + mDragAndDrop->drop(&drop, NULL); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - // remove object from the container it was coming from - mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); - mDragAndDrop->finish(); - mDragAndDrop->mSourceModel->update(); } else { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2b1cbe4c7..47cb50463 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1567,12 +1567,12 @@ namespace MWWorld item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } - bool World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) + MWWorld::Ptr World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) { std::pair result = mPhysics->castRay(cursorX, cursorY); if (!result.first) - return false; + return MWWorld::Ptr(); CellStore* cell = getPlayerPtr().getCell(); @@ -1593,7 +1593,7 @@ namespace MWWorld // only the player place items in the world, so no need to check actor PCDropped(dropped); - return true; + return dropped; } bool World::canPlaceObject(float cursorX, float cursorY) @@ -1644,7 +1644,7 @@ namespace MWWorld return dropped; } - void World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) + MWWorld::Ptr World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) { MWWorld::CellStore* cell = actor.getCell(); @@ -1673,6 +1673,7 @@ namespace MWWorld if(actor == mPlayer->getPlayer()) // Only call if dropped by player PCDropped(dropped); + return dropped; } void World::processChangedSettings(const Settings::CategorySettingVector& settings) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7b85c3ed8..490fdf5a7 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -439,15 +439,14 @@ namespace MWWorld virtual void update (float duration, bool paused); - virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); + virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place - /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount); + virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount); ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object From 868e38a07c5e1130e1ee3ce61866d3d0a6475def Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 May 2014 08:32:00 +0200 Subject: [PATCH 04/13] Fix Assault crimes being reported when striking in self-defense Last missing fix for Bug #1134. --- apps/openmw/mwclass/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 845fffbc0..6d2eea6af 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -621,7 +621,8 @@ namespace MWClass // Attacking peaceful NPCs is a crime // anything below 80 is considered peaceful (see Actors::updateActor) - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) + if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && + ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); getCreatureStats(ptr).setAttacked(true); From 028e00c98f6dc5274e6a5c005878f03bb4cebc65 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 May 2014 09:21:08 +0200 Subject: [PATCH 05/13] Add missing status report for some toggle commands --- apps/openmw/mwbase/windowmanager.hpp | 4 ++-- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 3 ++- apps/openmw/mwgui/mapwindow.hpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 3 ++- apps/openmw/mwgui/tooltips.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 8 ++++---- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++-- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwrender/water.cpp | 3 ++- apps/openmw/mwrender/water.hpp | 2 +- apps/openmw/mwscript/guiextensions.cpp | 6 ++++-- apps/openmw/mwscript/miscextensions.cpp | 3 ++- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- 16 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 39cfc47ed..44ebed3e9 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -193,9 +193,9 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual bool getWorldMouseOver() = 0; - virtual void toggleFogOfWar() = 0; + virtual bool toggleFogOfWar() = 0; - virtual void toggleFullHelp() = 0; + virtual bool toggleFullHelp() = 0; ///< show extra info in item tooltips (owner, script) virtual bool getFullHelp() const = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index e16f00186..2ddd01ac4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -126,7 +126,7 @@ namespace MWBase virtual void setWaterHeight(const float height) = 0; - virtual void toggleWater() = 0; + virtual bool toggleWater() = 0; virtual void adjustSky() = 0; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 2bd5e44cb..29c065f3d 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -89,10 +89,11 @@ namespace MWGui mChanged = true; } - void LocalMapBase::toggleFogOfWar() + bool LocalMapBase::toggleFogOfWar() { mFogOfWar = !mFogOfWar; applyFogOfWar(); + return mFogOfWar; } void LocalMapBase::applyFogOfWar() diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 5251fac23..d23b0c228 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -35,7 +35,7 @@ namespace MWGui void setPlayerDir(const float x, const float y); void setPlayerPos(const float x, const float y); - void toggleFogOfWar(); + bool toggleFogOfWar(); struct MarkerPosition { diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index f941c699b..b931966f0 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -547,9 +547,10 @@ namespace MWGui return " (" + boost::lexical_cast(value) + ")"; } - void ToolTips::toggleFullHelp() + bool ToolTips::toggleFullHelp() { mFullHelp = !mFullHelp; + return mFullHelp; } bool ToolTips::getFullHelp() const diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index be5c63191..4e73cc555 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -45,7 +45,7 @@ namespace MWGui void setEnabled(bool enabled); - void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) + bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script) bool getFullHelp() const; void setDelay(float delay); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ed6910221..3cf7379ff 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -814,10 +814,10 @@ namespace MWGui mHud->setMinimapVisible (visible); } - void WindowManager::toggleFogOfWar() + bool WindowManager::toggleFogOfWar() { mMap->toggleFogOfWar(); - mHud->toggleFogOfWar(); + return mHud->toggleFogOfWar(); } void WindowManager::setFocusObject(const MWWorld::Ptr& focus) @@ -830,9 +830,9 @@ namespace MWGui mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); } - void WindowManager::toggleFullHelp() + bool WindowManager::toggleFullHelp() { - mToolTips->toggleFullHelp(); + return mToolTips->toggleFullHelp(); } bool WindowManager::getFullHelp() const diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 7617a4463..c98c32c52 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -188,8 +188,8 @@ namespace MWGui virtual void setDragDrop(bool dragDrop); virtual bool getWorldMouseOver(); - virtual void toggleFogOfWar(); - virtual void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) + virtual bool toggleFogOfWar(); + virtual bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script) virtual bool getFullHelp() const; virtual void setActiveMap(int x, int y, bool interior); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a3c53dc44..9398e033a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -234,9 +234,9 @@ void RenderingManager::removeWater () mWater->setActive(false); } -void RenderingManager::toggleWater() +bool RenderingManager::toggleWater() { - mWater->toggle(); + return mWater->toggle(); } void RenderingManager::cellAdded (MWWorld::CellStore *store) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index eb6827292..d59e1e27d 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -127,7 +127,7 @@ public: void rotateObject (const MWWorld::Ptr& ptr); void setWaterHeight(const float height); - void toggleWater(); + bool toggleWater(); /// Updates object rendering after cell change /// \param old Object reference in previous cell diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 5368cbe68..2cbc4462c 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -326,10 +326,11 @@ void Water::setHeight(const float height) sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(height))); } -void Water::toggle() +bool Water::toggle() { mToggled = !mToggled; updateVisible(); + return mToggled; } void diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 481a41297..6a7b05a3a 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -150,7 +150,7 @@ namespace MWRender { void setActive(bool active); - void toggle(); + bool toggle(); void update(float dt, Ogre::Vector3 player); void frameStarted(float dt); diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 57fc2d470..be241a564 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -92,7 +92,8 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWBase::Environment::get().getWindowManager()->toggleFogOfWar(); + runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFogOfWar() ? "Fog of war -> On" + : "Fog of war -> Off"); } }; @@ -102,7 +103,8 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWBase::Environment::get().getWindowManager()->toggleFullHelp(); + runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFullHelp() ? "Full help -> On" + : "Full help -> Off"); } }; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 20013493f..620a0d0c9 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -275,7 +275,8 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWBase::Environment::get().getWorld()->toggleWater(); + runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWater() ? "Water -> On" + : "Water -> Off"); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 47cb50463..263478890 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1553,9 +1553,9 @@ namespace MWWorld mRendering->setWaterHeight(height); } - void World::toggleWater() + bool World::toggleWater() { - mRendering->toggleWater(); + return mRendering->toggleWater(); } void World::PCDropped (const Ptr& item) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 490fdf5a7..a86cebfd2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -213,7 +213,7 @@ namespace MWWorld virtual void setWaterHeight(const float height); - virtual void toggleWater(); + virtual bool toggleWater(); virtual void adjustSky(); From 36d9ae17cc07e27c9a7f4db8e8b8da24dfc09ac4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 May 2014 12:11:34 +0200 Subject: [PATCH 06/13] Revert "Change all AI packages (except AiActivate) to use ActorIds" Causes potential infinite loops (AiSequence::fill -> AiPackage() -> getCreatureStats -> ensureCustomData -> ..) This reverts commit 2e9985c1a371b0e5970f3791cc6d8298a624380a. --- apps/openmw/mwmechanics/actors.cpp | 6 +++--- apps/openmw/mwmechanics/aicombat.cpp | 29 +++++++++++--------------- apps/openmw/mwmechanics/aicombat.hpp | 6 +++--- apps/openmw/mwmechanics/aiescort.cpp | 21 ++++++------------- apps/openmw/mwmechanics/aiescort.hpp | 6 +++--- apps/openmw/mwmechanics/aifollow.cpp | 20 +++++++----------- apps/openmw/mwmechanics/aifollow.hpp | 22 +++++++++---------- apps/openmw/mwmechanics/aipursue.cpp | 11 +++++----- apps/openmw/mwmechanics/aipursue.hpp | 6 +++--- apps/openmw/mwmechanics/aisequence.cpp | 6 ++---- apps/openmw/mwscript/aiextensions.cpp | 14 +++++-------- 11 files changed, 61 insertions(+), 86 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a0616258c..108cc8cc5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -540,7 +540,7 @@ namespace MWMechanics MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); // Make the summoned creature follow its master and help in fights - AiFollow package(ptr); + AiFollow package(ptr.getRefData().getHandle()); summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); int creatureActorId = summonedCreatureStats.getActorId(); @@ -759,7 +759,7 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiPursue(player), ptr); + creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); creatureStats.setAlarmed(true); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); } @@ -788,7 +788,7 @@ namespace MWMechanics else if (!creatureStats.isHostile()) { if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPursue(player), ptr); + creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); else { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 72cb8f574..8464cc419 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -81,7 +81,7 @@ namespace MWMechanics // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp AiCombat::AiCombat(const MWWorld::Ptr& actor) : - mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()), + mTarget(actor), mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), @@ -153,9 +153,7 @@ namespace MWMechanics || actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); - - if(target.getClass().getCreatureStats(target).isDead()) + if(mTarget.getClass().getCreatureStats(mTarget).isDead()) return true; //Update every frame @@ -327,7 +325,7 @@ namespace MWMechanics ESM::Position pos = actor.getRefData().getPosition(); Ogre::Vector3 vActorPos(pos.pos); - Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos); + Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos); Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos; bool isStuck = false; @@ -398,7 +396,7 @@ namespace MWMechanics else // remote pathfinding { bool preferShortcut = false; - bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); + bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); if(mReadyToAttack) isStuck = false; @@ -434,7 +432,7 @@ namespace MWMechanics mFollowTarget = false; - buildNewPath(actor, target); //may fail to build a path, check before use + buildNewPath(actor); //may fail to build a path, check before use //delete visited path node mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]); @@ -478,9 +476,9 @@ namespace MWMechanics //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //then start attacking float speed1 = actorCls.getSpeed(actor); - float speed2 = target.getClass().getSpeed(target); - if(target.getClass().getMovementSettings(target).mPosition[0] == 0 - && target.getClass().getMovementSettings(target).mPosition[1] == 0) + float speed2 = mTarget.getClass().getSpeed(mTarget); + if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 + && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) speed2 = 0; float s1 = distToTarget - weapRange; @@ -572,9 +570,9 @@ namespace MWMechanics return false; } - void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) + void AiCombat::buildNewPath(const MWWorld::Ptr& actor) { - Ogre::Vector3 newPathTarget = Ogre::Vector3(target.getRefData().getPosition().pos); + Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos); float dist; @@ -629,12 +627,9 @@ namespace MWMechanics return 1; } - std::string AiCombat::getTargetId() const + const std::string &AiCombat::getTargetId() const { - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); - if (target.isEmpty()) - return ""; - return target.getRefData().getHandle(); + return mTarget.getRefData().getHandle(); } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 9125e2832..833b0063c 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -31,7 +31,7 @@ namespace MWMechanics virtual unsigned int getPriority() const; ///Returns target ID - std::string getTargetId() const; + const std::string &getTargetId() const; private: PathFinder mPathFinder; @@ -53,7 +53,7 @@ namespace MWMechanics ESM::Position mLastPos; MWMechanics::Movement mMovement; - int mTargetActorId; + MWWorld::Ptr mTarget; const MWWorld::CellStore* mCell; ObstacleCheck mObstacleCheck; @@ -63,7 +63,7 @@ namespace MWMechanics MWWorld::CellRefList::List::iterator mDoorIter; MWWorld::CellRefList& mDoors; - void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); + void buildNewPath(const MWWorld::Ptr& actor); }; } diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index f3ce7143a..0430adf06 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -8,8 +8,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" -#include "../mwmechanics/creaturestats.hpp" - #include "steering.hpp" #include "movement.hpp" @@ -21,8 +19,8 @@ namespace MWMechanics { - AiEscort::AiEscort(const MWWorld::Ptr& actor, int duration, float x, float y, float z) - : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mX(x), mY(y), mZ(z), mDuration(duration) + AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) + : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { @@ -40,8 +38,8 @@ namespace MWMechanics } } - AiEscort::AiEscort(const MWWorld::Ptr& actor, const std::string &cellId,int duration, float x, float y, float z) - : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) + AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) + : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { @@ -77,14 +75,7 @@ namespace MWMechanics return true; } - const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); - if (follower.isEmpty()) - { - // The follower disappeared - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; - } - + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos; double differenceBetween[3]; @@ -98,7 +89,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { - if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete + if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete return true; mMaxDist = 470; } diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index b8dc9d6e0..3771417fa 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -15,11 +15,11 @@ namespace MWMechanics /// Implementation of AiEscort /** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time \implement AiEscort **/ - AiEscort(const MWWorld::Ptr& actor,int duration, float x, float y, float z); + AiEscort(const std::string &actorId,int duration, float x, float y, float z); /// Implementation of AiEscortCell /** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time \implement AiEscortCell **/ - AiEscort(const MWWorld::Ptr& actor,const std::string &cellId,int duration, float x, float y, float z); + AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); virtual AiEscort *clone() const; @@ -28,7 +28,7 @@ namespace MWMechanics virtual int getTypeId() const; private: - int mActorId; + std::string mActorId; std::string mCellId; float mX; float mY; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index d747781a3..f1296a949 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -11,26 +11,23 @@ #include "steering.hpp" -MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(""), AiPackage() +MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), AiPackage() { - mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } -MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,const std::string &cellId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(cellId), AiPackage() +MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), AiPackage() { - mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } -MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor) -: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mCellId(""), AiPackage() +MWMechanics::AiFollow::AiFollow(const std::string &actorId) +: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage() { - mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist @@ -78,8 +75,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) std::string MWMechanics::AiFollow::getFollowedActor() { - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow - return target.getCellRef().mRefID; + return mActorId; } MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 91bdbdac2..10a381410 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -10,16 +10,16 @@ namespace MWMechanics { /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely - **/ - class AiFollow : public AiPackage - { + **/ + class AiFollow : public AiPackage + { public: /// Follow Actor for duration or until you arrive at a world position - AiFollow(const MWWorld::Ptr& actor,float duration, float X, float Y, float Z); + AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); /// Follow Actor for duration or until you arrive at a position in a cell - AiFollow(const MWWorld::Ptr& actor,const std::string &CellId,float duration, float X, float Y, float Z); + AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); /// Follow Actor indefinitively - AiFollow(const MWWorld::Ptr& actor); + AiFollow(const std::string &ActorId); virtual AiFollow *clone() const; @@ -38,8 +38,8 @@ namespace MWMechanics float mX; float mY; float mZ; - int mActorId; // The actor we should follow - std::string mCellId; - }; -} -#endif + std::string mActorId; + std::string mCellId; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index aa71af465..6e91ccb71 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -7,14 +7,12 @@ #include "../mwworld/action.hpp" #include "../mwworld/cellstore.hpp" -#include "../mwmechanics/creaturestats.hpp" - #include "steering.hpp" #include "movement.hpp" #include "creaturestats.hpp" -MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr& actor) - : mActorId(actor.getClass().getCreatureStats(actor).getActorId()) +MWMechanics::AiPursue::AiPursue(const std::string &objectId) + : mObjectId(objectId) { } MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const @@ -25,7 +23,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) { ESM::Position pos = actor.getRefData().getPosition(); //position of the actor - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist @@ -35,7 +33,8 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - target.getClass().activate(target,actor).get()->execute(actor); //Arrest player + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); + MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player return true; } else { diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 90ff8df93..2eb533d62 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -16,14 +16,14 @@ namespace MWMechanics { public: ///Constructor - /** \param actor Actor to pursue **/ - AiPursue(const MWWorld::Ptr& actor); + /** \param objectId Actor to pursue **/ + AiPursue(const std::string &objectId); virtual AiPursue *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual int getTypeId() const; private: - int mActorId; // The actor to pursue + std::string mObjectId; int mCellX; int mCellY; }; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 8afe8402f..0b1da180d 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -175,8 +175,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else if (it->mType == ESM::AI_Escort) { ESM::AITarget data = it->mTarget; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false); - package = new MWMechanics::AiEscort(target, data.mDuration, data.mX, data.mY, data.mZ); + package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Travel) { @@ -191,8 +190,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else //if (it->mType == ESM::AI_Follow) { ESM::AITarget data = it->mTarget; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false); - package = new MWMechanics::AiFollow(target, data.mDuration, data.mX, data.mY, data.mZ); + package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } mPackages.push_back(package); } diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8ed19925e..e53b53e58 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -91,7 +91,6 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); @@ -108,7 +107,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -147,7 +145,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); @@ -300,7 +297,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -339,8 +335,8 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i Date: Fri, 16 May 2014 12:20:36 +0200 Subject: [PATCH 07/13] Manually re-added AiCombat portion of actorid changes. This is the only one that really matters, and will not suffer from the infinite recursion because it's not included in AiSequence::fill. --- apps/openmw/mwmechanics/aicombat.cpp | 29 ++++++++++++++++------------ apps/openmw/mwmechanics/aicombat.hpp | 6 +++--- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8464cc419..72cb8f574 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -81,7 +81,7 @@ namespace MWMechanics // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp AiCombat::AiCombat(const MWWorld::Ptr& actor) : - mTarget(actor), + mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()), mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), @@ -153,7 +153,9 @@ namespace MWMechanics || actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; - if(mTarget.getClass().getCreatureStats(mTarget).isDead()) + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); + + if(target.getClass().getCreatureStats(target).isDead()) return true; //Update every frame @@ -325,7 +327,7 @@ namespace MWMechanics ESM::Position pos = actor.getRefData().getPosition(); Ogre::Vector3 vActorPos(pos.pos); - Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos); + Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos); Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos; bool isStuck = false; @@ -396,7 +398,7 @@ namespace MWMechanics else // remote pathfinding { bool preferShortcut = false; - bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); + bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); if(mReadyToAttack) isStuck = false; @@ -432,7 +434,7 @@ namespace MWMechanics mFollowTarget = false; - buildNewPath(actor); //may fail to build a path, check before use + buildNewPath(actor, target); //may fail to build a path, check before use //delete visited path node mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]); @@ -476,9 +478,9 @@ namespace MWMechanics //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //then start attacking float speed1 = actorCls.getSpeed(actor); - float speed2 = mTarget.getClass().getSpeed(mTarget); - if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 - && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + float speed2 = target.getClass().getSpeed(target); + if(target.getClass().getMovementSettings(target).mPosition[0] == 0 + && target.getClass().getMovementSettings(target).mPosition[1] == 0) speed2 = 0; float s1 = distToTarget - weapRange; @@ -570,9 +572,9 @@ namespace MWMechanics return false; } - void AiCombat::buildNewPath(const MWWorld::Ptr& actor) + void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { - Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos); + Ogre::Vector3 newPathTarget = Ogre::Vector3(target.getRefData().getPosition().pos); float dist; @@ -627,9 +629,12 @@ namespace MWMechanics return 1; } - const std::string &AiCombat::getTargetId() const + std::string AiCombat::getTargetId() const { - return mTarget.getRefData().getHandle(); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); + if (target.isEmpty()) + return ""; + return target.getRefData().getHandle(); } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 833b0063c..9125e2832 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -31,7 +31,7 @@ namespace MWMechanics virtual unsigned int getPriority() const; ///Returns target ID - const std::string &getTargetId() const; + std::string getTargetId() const; private: PathFinder mPathFinder; @@ -53,7 +53,7 @@ namespace MWMechanics ESM::Position mLastPos; MWMechanics::Movement mMovement; - MWWorld::Ptr mTarget; + int mTargetActorId; const MWWorld::CellStore* mCell; ObstacleCheck mObstacleCheck; @@ -63,7 +63,7 @@ namespace MWMechanics MWWorld::CellRefList::List::iterator mDoorIter; MWWorld::CellRefList& mDoors; - void buildNewPath(const MWWorld::Ptr& actor); + void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); }; } From e5a21aca53ba029e901066dd04b7d65289c19ee5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 May 2014 13:09:23 +0200 Subject: [PATCH 08/13] Refactor projectiles to no longer use MW-objects --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/soundmanager.hpp | 15 +- apps/openmw/mwbase/world.hpp | 5 +- apps/openmw/mwmechanics/spellcasting.cpp | 55 +++- apps/openmw/mwrender/effectmanager.cpp | 12 - apps/openmw/mwrender/effectmanager.hpp | 14 + apps/openmw/mwsound/soundmanagerimp.cpp | 38 +++ apps/openmw/mwsound/soundmanagerimp.hpp | 10 + apps/openmw/mwworld/projectilemanager.cpp | 261 +++++++++++++++++ apps/openmw/mwworld/projectilemanager.hpp | 104 +++++++ apps/openmw/mwworld/scene.cpp | 3 - apps/openmw/mwworld/worldimp.cpp | 335 ++-------------------- apps/openmw/mwworld/worldimp.hpp | 47 +-- 13 files changed, 528 insertions(+), 373 deletions(-) create mode 100644 apps/openmw/mwworld/projectilemanager.cpp create mode 100644 apps/openmw/mwworld/projectilemanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index aee5cbeac..e791ab891 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -57,7 +57,7 @@ add_openmw_dir (mwworld cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor - contentloader esmloader omwloader actiontrap cellreflist + contentloader esmloader omwloader actiontrap cellreflist projectilemanager ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index f3973153a..15739730b 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -110,18 +110,25 @@ namespace MWBase ///< Play a sound, independently of 3D-position ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. - virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, - float volume, float pitch, PlayType type=Play_TypeSfx, - PlayMode mode=Play_Normal, float offset=0) = 0; - ///< Play a sound from an object + virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, + float volume, float pitch, PlayType type=Play_TypeSfx, + PlayMode mode=Play_Normal, float offset=0) = 0; + ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type, PlayMode mode, float offset=0) = 0; + ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition. + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, virtual void stopSound3D(const MWWorld::Ptr &reference) = 0; ///< Stop the given object from playing all sounds. + virtual void stopSound(MWBase::SoundPtr sound) = 0; + ///< Stop the given sound handle + virtual void stopSound(const MWWorld::CellStore *cell) = 0; ///< Stop all sounds for the given cell. diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 2ddd01ac4..d1ecd11a0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -467,7 +467,8 @@ namespace MWBase virtual void castSpell (const MWWorld::Ptr& actor) = 0; - virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, + virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, + float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) = 0; virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; @@ -513,7 +514,7 @@ namespace MWBase /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; - virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; }; } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0843e3e54..91a81c74b 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -21,6 +21,41 @@ #include "magiceffects.hpp" #include "npcstats.hpp" +namespace +{ + + /// Get projectile properties (model, sound and speed) for a spell with the given effects + /// If \a model is empty, the spell has no ranged effects and should not spawn a projectile. + void getProjectileInfo (const ESM::EffectList& effects, std::string& model, std::string& sound, float& speed) + { + for (std::vector::const_iterator iter (effects.mList.begin()); + iter!=effects.mList.end(); ++iter) + { + if (iter->mRange != ESM::RT_Target) + continue; + + const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( + iter->mEffectID); + + model = magicEffect->mBolt; + if (model.empty()) + model = "VFX_DefaultBolt"; + + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + if (!magicEffect->mBoltSound.empty()) + sound = magicEffect->mBoltSound; + else + sound = schools[magicEffect->mData.mSchool] + " bolt"; + + speed = magicEffect->mData.mSpeed; + break; + } + } + +} + namespace MWMechanics { @@ -409,7 +444,7 @@ namespace MWMechanics } if (!exploded) - MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, mTarget, effects, caster, mId, mSourceName); + MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, mId, mSourceName); if (!reflectedEffects.mList.empty()) inflict(caster, target, reflectedEffects, range, true); @@ -603,7 +638,13 @@ namespace MWMechanics inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); } - MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName); + std::string projectileModel; + std::string sound; + float speed = 0; + getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed); + if (!projectileModel.empty()) + MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, + false, enchantment->mEffects, mCaster, mSourceName); return true; } @@ -682,7 +723,15 @@ namespace MWMechanics } } - MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName); + + std::string projectileModel; + std::string sound; + float speed = 0; + getProjectileInfo(spell->mEffects, projectileModel, sound, speed); + if (!projectileModel.empty()) + MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, + false, spell->mEffects, mCaster, mSourceName); + return true; } diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index 1e6119daa..968be0f9e 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -11,18 +11,6 @@ namespace MWRender { -class EffectAnimationTime : public Ogre::ControllerValue -{ -private: - float mTime; -public: - EffectAnimationTime() : mTime(0) { } - void addTime(float time) { mTime += time; } - - virtual Ogre::Real getValue() const { return mTime; } - virtual void setValue(Ogre::Real value) {} -}; - EffectManager::EffectManager(Ogre::SceneManager *sceneMgr) : mSceneMgr(sceneMgr) { diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp index bc9e68d26..eb6863655 100644 --- a/apps/openmw/mwrender/effectmanager.hpp +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -5,6 +5,20 @@ namespace MWRender { + + class EffectAnimationTime : public Ogre::ControllerValue + { + private: + float mTime; + public: + EffectAnimationTime() : mTime(0) { } + void addTime(float time) { mTime += time; } + + virtual Ogre::Real getValue() const { return mTime; } + virtual void setValue(Ogre::Real value) {} + }; + + // Note: effects attached to another object should be managed by MWRender::Animation::addEffect. // This class manages "free" effects, i.e. attached to a dedicated scene node in the world. class EffectManager diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8ce87a25e..dc31772c9 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -356,6 +356,44 @@ namespace MWSound return sound; } + MWBase::SoundPtr SoundManager::playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type, PlayMode mode, float offset) + { + MWBase::SoundPtr sound; + if(!mOutput->isInitialized()) + return sound; + try + { + // Look up the sound in the ESM data + float basevol = volumeFromType(type); + float min, max; + std::string file = lookup(soundId, volume, min, max); + + sound = mOutput->playSound3D(file, initialPos, volume, basevol, pitch, min, max, mode|type, offset); + mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); + } + catch(std::exception &e) + { + //std::cout <<"Sound Error: "<first == sound) + { + snditer->first->stop(); + mActiveSounds.erase(snditer++); + } + else + ++snditer; + } + } + void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId) { SoundMap::iterator snditer = mActiveSounds.begin(); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 1454240b4..ab9dcf734 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -115,6 +115,13 @@ namespace MWSound virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); + ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. + ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + + virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type, PlayMode mode, float offset=0); + ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition. + ///< Play a sound from an object ///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end. @@ -124,6 +131,9 @@ namespace MWSound virtual void stopSound3D(const MWWorld::Ptr &reference); ///< Stop the given object from playing all sounds. + virtual void stopSound(MWBase::SoundPtr sound); + ///< Stop the given sound handle + virtual void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp new file mode 100644 index 000000000..c886908f9 --- /dev/null +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -0,0 +1,261 @@ +#include "projectilemanager.hpp" + +#include + +#include + +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" + +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwmechanics/combat.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellcasting.hpp" + +#include "../mwrender/effectmanager.hpp" + +#include "../mwsound/sound.hpp" + +namespace MWWorld +{ + + ProjectileManager::ProjectileManager(Ogre::SceneManager* sceneMgr, OEngine::Physic::PhysicEngine &engine) + : mPhysEngine(engine) + , mSceneMgr(sceneMgr) + { + + } + + void ProjectileManager::createModel(State &state, const std::string &model) + { + state.mObject = NifOgre::Loader::createObjects(state.mNode, model); + for(size_t i = 0;i < state.mObject->mControllers.size();i++) + { + if(state.mObject->mControllers[i].getSource().isNull()) + state.mObject->mControllers[i].setSource(Ogre::SharedPtr (new MWRender::EffectAnimationTime())); + } + } + + void ProjectileManager::update(NifOgre::ObjectScenePtr object, float duration) + { + for(size_t i = 0; i < object->mControllers.size() ;i++) + { + MWRender::EffectAnimationTime* value = dynamic_cast(object->mControllers[i].getSource().get()); + if (value) + value->addTime(duration); + + object->mControllers[i].update(); + } + } + + void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound, + const std::string &spellId, float speed, bool stack, + const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName) + { + // Spawn at 0.75 * ActorHeight + float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75; + + Ogre::Vector3 pos(actor.getRefData().getPosition().pos); + pos.z += height; + + Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + + MagicBoltState state; + state.mSourceName = sourceName; + state.mId = spellId; + state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); + state.mSpeed = speed; + state.mStack = stack; + + // Only interested in "on target" effects + for (std::vector::const_iterator iter (effects.mList.begin()); + iter!=effects.mList.end(); ++iter) + { + if (iter->mRange == ESM::RT_Target) + state.mEffects.mList.push_back(*iter); + } + + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), model); + MWWorld::Ptr ptr = ref.getPtr(); + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient); + createModel(state, ptr.getClass().getModel(ptr)); + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + state.mSound = sndMgr->playManualSound3D(pos, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + + mMagicBolts.push_back(state); + } + + void ProjectileManager::launchProjectile(Ptr actor, Ptr projectile, const Ogre::Vector3 &pos, + const Ogre::Quaternion &orient, Ptr bow, float speed) + { + ProjectileState state; + state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); + state.mBow = bow; + state.mVelocity = orient.yAxis() * speed; + state.mProjectileId = projectile.getCellRef().mRefID; + + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().mRefID); + MWWorld::Ptr ptr = ref.getPtr(); + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient); + createModel(state, ptr.getClass().getModel(ptr)); + + mProjectiles.push_back(state); + } + + void ProjectileManager::update(float dt) + { + moveProjectiles(dt); + moveMagicBolts(dt); + } + + void ProjectileManager::moveMagicBolts(float duration) + { + for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) + { + Ogre::Quaternion orient = it->mNode->getOrientation(); + static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() + .find("fTargetSpellMaxSpeed")->getFloat(); + float speed = fTargetSpellMaxSpeed * it->mSpeed; + + Ogre::Vector3 direction = orient.yAxis(); + direction.normalise(); + Ogre::Vector3 pos(it->mNode->getPosition()); + Ogre::Vector3 newPos = pos + direction * duration * speed; + + it->mSound->setPosition(newPos); + + it->mNode->setPosition(newPos); + + update(it->mObject, duration); + + // Check for impact + // TODO: use a proper btRigidBody / btGhostObject? + btVector3 from(pos.x, pos.y, pos.z); + btVector3 to(newPos.x, newPos.y, newPos.z); + std::vector > collisions = mPhysEngine.rayTest2(from, to); + bool hit=false; + + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) + { + MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); + + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + if (caster.isEmpty()) + caster = obstacle; + + if (obstacle.isEmpty()) + { + // Terrain + } + else + { + MWMechanics::CastSpell cast(caster, obstacle); + cast.mHitPosition = pos; + cast.mId = it->mId; + cast.mSourceName = it->mSourceName; + cast.mStack = it->mStack; + cast.inflict(obstacle, caster, it->mEffects, ESM::RT_Target, false, true); + } + + hit = true; + } + if (hit) + { + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mId, it->mSourceName); + + MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); + + mSceneMgr->destroySceneNode(it->mNode); + + it = mMagicBolts.erase(it); + continue; + } + else + ++it; + } + } + + void ProjectileManager::moveProjectiles(float duration) + { + for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end();) + { + // gravity constant - must be way lower than the gravity affecting actors, since we're not + // simulating aerodynamics at all + it->mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration; + + Ogre::Vector3 pos(it->mNode->getPosition()); + Ogre::Vector3 newPos = pos + it->mVelocity * duration; + + Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->mVelocity); + it->mNode->setOrientation(orient); + it->mNode->setPosition(newPos); + + update(it->mObject, duration); + + // Check for impact + // TODO: use a proper btRigidBody / btGhostObject? + btVector3 from(pos.x, pos.y, pos.z); + btVector3 to(newPos.x, newPos.y, newPos.z); + std::vector > collisions = mPhysEngine.rayTest2(from, to); + bool hit=false; + + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) + { + MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); + + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + + // Arrow intersects with player immediately after shooting :/ + if (obstacle == caster) + continue; + + if (caster.isEmpty()) + caster = obstacle; + + if (obstacle.isEmpty()) + { + // Terrain + } + else if (obstacle.getClass().isActor()) + { + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mProjectileId); + MWMechanics::projectileHit(caster, obstacle, it->mBow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first); + } + hit = true; + } + if (hit) + { + mSceneMgr->destroySceneNode(it->mNode); + + it = mProjectiles.erase(it); + continue; + } + else + ++it; + } + } + + void ProjectileManager::clear() + { + for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + { + mSceneMgr->destroySceneNode(it->mNode); + } + mProjectiles.clear(); + for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) + { + MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); + mSceneMgr->destroySceneNode(it->mNode); + } + mMagicBolts.clear(); + } + +} diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp new file mode 100644 index 000000000..e934bf6ab --- /dev/null +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -0,0 +1,104 @@ +#ifndef OPENMW_MWWORLD_PROJECTILEMANAGER_H +#define OPENMW_MWWORLD_PROJECTILEMANAGER_H + +#include + +#include + +#include +#include + +#include "../mwbase/soundmanager.hpp" + +#include "ptr.hpp" + +namespace OEngine +{ +namespace Physic +{ + class PhysicEngine; +} +} + +namespace Ogre +{ + class SceneManager; +} + +namespace MWWorld +{ + + class ProjectileManager + { + public: + ProjectileManager (Ogre::SceneManager* sceneMgr, + OEngine::Physic::PhysicEngine& engine); + + void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, + float speed, bool stack, const ESM::EffectList& effects, + const MWWorld::Ptr& actor, const std::string& sourceName); + + void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); + + void update(float dt); + + /// Removes all current projectiles. Should be called when switching to a new worldspace. + void clear(); + + private: + OEngine::Physic::PhysicEngine& mPhysEngine; + Ogre::SceneManager* mSceneMgr; + + struct State + { + NifOgre::ObjectScenePtr mObject; + Ogre::SceneNode* mNode; + }; + + struct MagicBoltState : public State + { + // Id of spell or enchantment to apply when it hits + std::string mId; + + // Actor who casted this projectile + int mActorId; + + // Name of item to display as effect source in magic menu (in case we casted an enchantment) + std::string mSourceName; + + ESM::EffectList mEffects; + + float mSpeed; + + bool mStack; + + MWBase::SoundPtr mSound; + }; + + struct ProjectileState : public State + { + // Actor who shot this projectile + int mActorId; + + // RefID of the projectile + std::string mProjectileId; + + MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from (may be empty) + + Ogre::Vector3 mVelocity; + }; + + std::vector mMagicBolts; + std::vector mProjectiles; + + void moveProjectiles(float dt); + void moveMagicBolts(float dt); + + void createModel (State& state, const std::string& model); + void update (NifOgre::ObjectScenePtr object, float duration); + }; + +} + +#endif diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 4b92a920b..ecc8bdd02 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -344,8 +344,6 @@ namespace MWWorld // Sky system MWBase::Environment::get().getWorld()->adjustSky(); - mRendering.switchToExterior(); - mCellChanged = true; loadingListener->removeWallpaper(); @@ -439,7 +437,6 @@ namespace MWWorld mCurrentCell = cell; // adjust fog - mRendering.switchToInterior(); mRendering.configureFog(*mCurrentCell); // adjust player diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 263478890..587fb71f4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -43,6 +43,7 @@ #include "containerstore.hpp" #include "inventorystore.hpp" #include "actionteleport.hpp" +#include "projectilemanager.hpp" #include "contentloader.hpp" #include "esmloader.hpp" @@ -136,6 +137,8 @@ namespace MWWorld mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); + mProjectileManager.reset(new ProjectileManager(renderer.getScene(), *mPhysEngine)); + mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback); mPhysEngine->setSceneManager(renderer.getScene()); @@ -255,8 +258,6 @@ namespace MWWorld mCells.clear(); - mMagicBolts.clear(); - mProjectiles.clear(); mDoorStates.clear(); mGodMode = false; @@ -807,6 +808,10 @@ namespace MWWorld void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + // changed worldspace + mProjectileManager->clear(); + mRendering->switchToInterior(); + removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); @@ -814,6 +819,12 @@ namespace MWWorld void World::changeToExteriorCell (const ESM::Position& position) { + if (!getPlayerPtr().getCell()->getCell()->isExterior()) + { + // changed worldspace + mProjectileManager->clear(); + mRendering->switchToExterior(); + } removeContainerScripts(getPlayerPtr()); mWorldScene->changeToExteriorCell(position); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); @@ -949,8 +960,6 @@ namespace MWWorld MWWorld::Ptr newPtr = MWWorld::Class::get(ptr) .copyToCell(ptr, *newCell); newPtr.getRefData().setBaseNode(0); - - objectLeftActiveCell(ptr, newPtr); } else { @@ -1172,8 +1181,7 @@ namespace MWWorld { processDoors(duration); - moveMagicBolts(duration); - moveProjectiles(duration); + mProjectileManager->update(duration); const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); PtrVelocityList::const_iterator player(results.end()); @@ -2254,306 +2262,14 @@ namespace MWWorld void World::launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) { - ProjectileState state; - state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); - state.mBow = bow; - state.mVelocity = orient.yAxis() * speed; - - MWWorld::ManualRef ref(getStore(), projectile.getCellRef().mRefID); - - ESM::Position pos; - pos.pos[0] = worldPos.x; - pos.pos[1] = worldPos.y; - pos.pos[2] = worldPos.z; - - // Do NOT copy actor rotation! actors use a different rotation order, and this will not produce the same facing direction. - Ogre::Matrix3 mat; - orient.ToRotationMatrix(mat); - Ogre::Radian xr,yr,zr; - mat.ToEulerAnglesXYZ(xr, yr, zr); - pos.rot[0] = -xr.valueRadians(); - pos.rot[1] = -yr.valueRadians(); - pos.rot[2] = -zr.valueRadians(); - - MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), actor.getCell(), pos, false); - ptr.getRefData().setCount(1); - - mProjectiles[ptr] = state; + mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed); } - void World::launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, + void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, + float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) { - std::string projectileModel; - std::string sound; - float speed = 0; - for (std::vector::const_iterator iter (effects.mList.begin()); - iter!=effects.mList.end(); ++iter) - { - if (iter->mRange != ESM::RT_Target) - continue; - - const ESM::MagicEffect *magicEffect = getStore().get().find ( - iter->mEffectID); - - projectileModel = magicEffect->mBolt; - if (projectileModel.empty()) - projectileModel = "VFX_DefaultBolt"; - - static const std::string schools[] = { - "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" - }; - - if (!magicEffect->mBoltSound.empty()) - sound = magicEffect->mBoltSound; - else - sound = schools[magicEffect->mData.mSchool] + " bolt"; - - speed = magicEffect->mData.mSpeed; - break; - } - if (projectileModel.empty()) - return; - - // Spawn at 0.75 * ActorHeight - float height = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75; - - MWWorld::ManualRef ref(getStore(), projectileModel); - ESM::Position pos; - pos.pos[0] = actor.getRefData().getPosition().pos[0]; - pos.pos[1] = actor.getRefData().getPosition().pos[1]; - pos.pos[2] = actor.getRefData().getPosition().pos[2] + height; - - // Do NOT copy rotation directly! actors use a different rotation order, and this will not produce the same facing direction. - Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); - Ogre::Matrix3 mat; - orient.ToRotationMatrix(mat); - Ogre::Radian xr,yr,zr; - mat.ToEulerAnglesXYZ(xr, yr, zr); - pos.rot[0] = -xr.valueRadians(); - pos.rot[1] = -yr.valueRadians(); - pos.rot[2] = -zr.valueRadians(); - - ref.getPtr().getCellRef().mPos = pos; - CellStore* cell = actor.getCell(); - MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), cell, pos); - - MagicBoltState state; - state.mSourceName = sourceName; - state.mId = id; - state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); - state.mSpeed = speed; - state.mStack = stack; - - // Only interested in "on target" effects - for (std::vector::const_iterator iter (effects.mList.begin()); - iter!=effects.mList.end(); ++iter) - { - if (iter->mRange == ESM::RT_Target) - state.mEffects.mList.push_back(*iter); - } - - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); - - mMagicBolts[ptr] = state; - } - - void World::moveProjectiles(float duration) - { - std::map moved; - for (std::map::iterator it = mProjectiles.begin(); it != mProjectiles.end();) - { - if (!mWorldScene->isCellActive(*it->first.getCell())) - { - deleteObject(it->first); - mProjectiles.erase(it++); - continue; - } - - MWWorld::Ptr ptr = it->first; - - // gravity constant - must be way lower than the gravity affecting actors, since we're not - // simulating aerodynamics at all - it->second.mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration; - - Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); - Ogre::Vector3 newPos = pos + it->second.mVelocity * duration; - - Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->second.mVelocity); - Ogre::Matrix3 mat; - orient.ToRotationMatrix(mat); - Ogre::Radian xr,yr,zr; - mat.ToEulerAnglesXYZ(xr, yr, zr); - rotateObject(ptr, -xr.valueDegrees(), -yr.valueDegrees(), -zr.valueDegrees()); - - // Check for impact - btVector3 from(pos.x, pos.y, pos.z); - btVector3 to(newPos.x, newPos.y, newPos.z); - std::vector > collisions = mPhysEngine->rayTest2(from, to); - bool hit=false; - - // HACK: query against the shape as well, since the ray does not take the volume into account - // really, this should be a convex cast, but the whole physics system needs a rewrite - std::vector col2 = mPhysEngine->getCollisions(ptr.getRefData().getHandle()); - for (std::vector::iterator ci = col2.begin(); ci != col2.end(); ++ci) - collisions.push_back(std::make_pair(0.f,*ci)); - - for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) - { - MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second); - if (obstacle == ptr) - continue; - - MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId); - - // Arrow intersects with player immediately after shooting :/ - if (obstacle == caster) - continue; - - if (caster.isEmpty()) - caster = obstacle; - - if (obstacle.isEmpty()) - { - // Terrain - } - else if (obstacle.getClass().isActor()) - { - MWMechanics::projectileHit(caster, obstacle, it->second.mBow, ptr, pos + (newPos - pos) * cIt->first); - } - hit = true; - } - if (hit) - { - deleteObject(ptr); - mProjectiles.erase(it++); - continue; - } - - std::string handle = ptr.getRefData().getHandle(); - - moveObject(ptr, newPos.x, newPos.y, newPos.z); - - // HACK: Re-fetch Ptrs if necessary, since the cell might have changed - if (!ptr.getRefData().getCount()) - { - moved[handle] = it->second; - mProjectiles.erase(it++); - } - else - ++it; - } - - // HACK: Re-fetch Ptrs if necessary, since the cell might have changed - for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) - { - MWWorld::Ptr newPtr = searchPtrViaHandle(it->first); - if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted - continue; - mProjectiles[getPtrViaHandle(it->first)] = it->second; - } - } - - void World::moveMagicBolts(float duration) - { - std::map moved; - for (std::map::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) - { - if (!mWorldScene->isCellActive(*it->first.getCell())) - { - deleteObject(it->first); - mMagicBolts.erase(it++); - continue; - } - - MWWorld::Ptr ptr = it->first; - - Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - - Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); - static float fTargetSpellMaxSpeed = getStore().get().find("fTargetSpellMaxSpeed")->getFloat(); - float speed = fTargetSpellMaxSpeed * it->second.mSpeed; - - Ogre::Vector3 direction = orient.yAxis(); - direction.normalise(); - Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); - Ogre::Vector3 newPos = pos + direction * duration * speed; - - // Check for impact - btVector3 from(pos.x, pos.y, pos.z); - btVector3 to(newPos.x, newPos.y, newPos.z); - std::vector > collisions = mPhysEngine->rayTest2(from, to); - bool explode = false; - for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !explode; ++cIt) - { - MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second); - if (obstacle == ptr) - continue; - - MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId); - if (caster.isEmpty()) - caster = obstacle; - - if (obstacle.isEmpty()) - { - // Terrain - } - else - { - MWMechanics::CastSpell cast(caster, obstacle); - cast.mHitPosition = pos; - cast.mId = it->second.mId; - cast.mSourceName = it->second.mSourceName; - cast.mStack = it->second.mStack; - cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false, true); - } - - explode = true; - } - - if (explode) - { - MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId); - explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName); - - deleteObject(ptr); - mMagicBolts.erase(it++); - continue; - } - - std::string handle = ptr.getRefData().getHandle(); - - moveObject(ptr, newPos.x, newPos.y, newPos.z); - - // HACK: Re-fetch Ptrs if necessary, since the cell might have changed - if (!ptr.getRefData().getCount()) - { - moved[handle] = it->second; - mMagicBolts.erase(it++); - } - else - ++it; - } - - // HACK: Re-fetch Ptrs if necessary, since the cell might have changed - for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) - { - MWWorld::Ptr newPtr = searchPtrViaHandle(it->first); - if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted - continue; - mMagicBolts[getPtrViaHandle(it->first)] = it->second; - } - } - - void World::objectLeftActiveCell(Ptr object, Ptr movedPtr) - { - // For now, projectiles moved to an inactive cell are just deleted, because there's no reliable way to hold on to the meta information - if (mMagicBolts.find(object) != mMagicBolts.end()) - deleteObject(movedPtr); - if (mProjectiles.find(object) != mProjectiles.end()) - deleteObject(movedPtr); + mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName); } const std::vector& World::getContentFiles() const @@ -2935,7 +2651,7 @@ namespace MWWorld mRendering->spawnEffect(model, texture, worldPosition); } - void World::explodeSpell(const Vector3 &origin, const MWWorld::Ptr& object, const ESM::EffectList &effects, const Ptr &caster, + void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, const std::string& id, const std::string& sourceName) { std::map > toApply; @@ -2960,12 +2676,13 @@ namespace MWWorld static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(!effect->mAreaSound.empty()) - sndMgr->playSound3D(object, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - else - sndMgr->playSound3D(object, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playManualSound3D(origin, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + else + sndMgr->playManualSound3D(origin, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + } // Get the actors in range of the effect std::vector objects; MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a86cebfd2..8d2f3a9da 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -3,6 +3,8 @@ #include "../mwrender/debugging.hpp" +#include + #include "ptr.hpp" #include "scene.hpp" #include "esmstore.hpp" @@ -51,6 +53,7 @@ namespace MWWorld { class WeatherManager; class Player; + class ProjectileManager; /// \brief The game world and its visual representation @@ -74,6 +77,8 @@ namespace MWWorld OEngine::Physic::PhysicEngine* mPhysEngine; + boost::shared_ptr mProjectileManager; + bool mGodMode; std::vector mContentFiles; @@ -90,37 +95,6 @@ namespace MWWorld std::map mDoorStates; ///< only holds doors that are currently moving. 1 = opening, 2 = closing - struct MagicBoltState - { - // Id of spell or enchantment to apply when it hits - std::string mId; - - // Actor who casted this projectile - int mActorId; - - // Name of item to display as effect source in magic menu (in case we casted an enchantment) - std::string mSourceName; - - ESM::EffectList mEffects; - - float mSpeed; - - bool mStack; - }; - - struct ProjectileState - { - // Actor who shot this projectile - int mActorId; - - MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from - - Ogre::Vector3 mVelocity; - }; - - std::map mMagicBolts; - std::map mProjectiles; - std::string mStartCell; void updateWeather(float duration); @@ -148,9 +122,6 @@ namespace MWWorld void processDoors(float duration); ///< Run physics simulation and modify \a world accordingly. - void moveMagicBolts(float duration); - void moveProjectiles(float duration); - void doPhysics(float duration); ///< Run physics simulation and modify \a world accordingly. @@ -171,9 +142,6 @@ namespace MWWorld bool mLevitationEnabled; bool mGoToJail; - /// Called when \a object is moved to an inactive cell - void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr); - float feetToGameUnits(float feet); public: @@ -572,7 +540,8 @@ namespace MWWorld */ virtual void castSpell (const MWWorld::Ptr& actor); - virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, + virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, + float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName); virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); @@ -612,7 +581,7 @@ namespace MWWorld /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition); - virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); }; } From 9adb990143b8121e8e57446a24f5f4cfecb10f4c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 May 2014 02:52:10 +0200 Subject: [PATCH 09/13] Fix potentially unsafe use of MWWorld::Ptr --- apps/openmw/mwworld/projectilemanager.cpp | 24 +++++++++++++++++------ apps/openmw/mwworld/projectilemanager.hpp | 3 ++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index c886908f9..55ecdb569 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -6,6 +6,7 @@ #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" @@ -96,7 +97,7 @@ namespace MWWorld { ProjectileState state; state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); - state.mBow = bow; + state.mBowId = bow.getCellRef().mRefID; state.mVelocity = orient.yAxis() * speed; state.mProjectileId = projectile.getCellRef().mRefID; @@ -217,17 +218,28 @@ namespace MWWorld if (obstacle == caster) continue; - if (caster.isEmpty()) - caster = obstacle; - if (obstacle.isEmpty()) { // Terrain } else if (obstacle.getClass().isActor()) - { + { MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mProjectileId); - MWMechanics::projectileHit(caster, obstacle, it->mBow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first); + + // Try to get a Ptr to the bow that was used. It might no longer exist. + MWWorld::Ptr bow = projectileRef.getPtr(); + if (!caster.isEmpty()) + { + MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); + MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().mRefID, it->mBowId)) + bow = *invIt; + } + + if (caster.isEmpty()) + caster = obstacle; + + MWMechanics::projectileHit(caster, obstacle, bow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first); } hit = true; } diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index e934bf6ab..c51985a7e 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -84,7 +84,8 @@ namespace MWWorld // RefID of the projectile std::string mProjectileId; - MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from (may be empty) + // RefID of the bow or crossbow the actor was using when this projectile was fired (may be empty) + std::string mBowId; Ogre::Vector3 mVelocity; }; From 15368188dc9d55f084ee14125c8d01d71e960a91 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 May 2014 02:54:51 +0200 Subject: [PATCH 10/13] Fix sliders in effect editor not being precisely movable --- files/mygui/openmw_edit_effect.layout | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/files/mygui/openmw_edit_effect.layout b/files/mygui/openmw_edit_effect.layout index fa1e58b9d..5dc53e505 100644 --- a/files/mygui/openmw_edit_effect.layout +++ b/files/mygui/openmw_edit_effect.layout @@ -33,6 +33,7 @@ + @@ -41,6 +42,7 @@ + @@ -58,6 +60,7 @@ + @@ -74,6 +77,7 @@ + From e266aff5619a6280c93a2d8ecdd8e51f341b1093 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 May 2014 05:21:17 +0200 Subject: [PATCH 11/13] Savegame: store projectiles --- apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 10 +- apps/openmw/mwrender/renderingmanager.hpp | 3 +- apps/openmw/mwstate/statemanagerimp.cpp | 8 +- apps/openmw/mwworld/projectilemanager.cpp | 137 +++++++++++++++++++++- apps/openmw/mwworld/projectilemanager.hpp | 28 +++-- apps/openmw/mwworld/worldimp.cpp | 26 ++-- apps/openmw/mwworld/worldimp.hpp | 5 +- components/CMakeLists.txt | 2 +- components/esm/defs.hpp | 2 + components/esm/projectilestate.cpp | 65 ++++++++++ components/esm/projectilestate.hpp | 90 ++++++++++++++ 13 files changed, 343 insertions(+), 38 deletions(-) create mode 100644 components/esm/projectilestate.cpp create mode 100644 components/esm/projectilestate.hpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d1ecd11a0..81bec6fe8 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -255,7 +255,8 @@ namespace MWBase virtual void changeToExteriorCell (const ESM::Position& position) = 0; ///< Move to exterior cell. - virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0; + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true) = 0; + ///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 108cc8cc5..d54805539 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -785,7 +785,7 @@ namespace MWMechanics // Update witness crime id npcStats.setCrimeId(-1); } - else if (!creatureStats.isHostile()) + else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue) { if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9398e033a..991ca690e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -694,15 +694,8 @@ Shadows* RenderingManager::getShadows() return mShadows; } -void RenderingManager::switchToInterior() +void RenderingManager::notifyWorldSpaceChanged() { - // TODO: also do this when switching worldspace - mEffectManager->clear(); -} - -void RenderingManager::switchToExterior() -{ - // TODO: also do this when switching worldspace mEffectManager->clear(); } @@ -1061,6 +1054,7 @@ void RenderingManager::spawnEffect(const std::string &model, const std::string & void RenderingManager::clear() { mLocalMap->clear(); + notifyWorldSpaceChanged(); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d59e1e27d..f539f9270 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -163,8 +163,7 @@ public: Shadows* getShadows(); - void switchToInterior(); - void switchToExterior(); + void notifyWorldSpaceChanged(); void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f2cda5a01..304228010 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -319,6 +319,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_WTHR: case ESM::REC_DYNA: case ESM::REC_ACTC: + case ESM::REC_PROJ: + case ESM::REC_MPRJ: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; @@ -361,7 +363,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); + + // Do not trigger erroneous cellChanged events + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); } catch (const std::exception& e) { diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 55ecdb569..266b566f7 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -4,6 +4,8 @@ #include +#include + #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -67,10 +69,12 @@ namespace MWWorld MagicBoltState state; state.mSourceName = sourceName; - state.mId = spellId; + state.mId = model; + state.mSpellId = spellId; state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); state.mSpeed = speed; state.mStack = stack; + state.mSoundId = sound; // Only interested in "on target" effects for (std::vector::const_iterator iter (effects.mList.begin()); @@ -99,7 +103,7 @@ namespace MWWorld state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); state.mBowId = bow.getCellRef().mRefID; state.mVelocity = orient.yAxis() * speed; - state.mProjectileId = projectile.getCellRef().mRefID; + state.mId = projectile.getCellRef().mRefID; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().mRefID); MWWorld::Ptr ptr = ref.getPtr(); @@ -159,7 +163,7 @@ namespace MWWorld { MWMechanics::CastSpell cast(caster, obstacle); cast.mHitPosition = pos; - cast.mId = it->mId; + cast.mId = it->mSpellId; cast.mSourceName = it->mSourceName; cast.mStack = it->mStack; cast.inflict(obstacle, caster, it->mEffects, ESM::RT_Target, false, true); @@ -170,7 +174,7 @@ namespace MWWorld if (hit) { MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); - MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mId, it->mSourceName); + MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mSpellId, it->mSourceName); MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); @@ -224,7 +228,7 @@ namespace MWWorld } else if (obstacle.getClass().isActor()) { - MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mProjectileId); + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId); // Try to get a Ptr to the bow that was used. It might no longer exist. MWWorld::Ptr bow = projectileRef.getPtr(); @@ -270,4 +274,127 @@ namespace MWWorld mMagicBolts.clear(); } + void ProjectileManager::write(ESM::ESMWriter &writer, Loading::Listener &progress) const + { + for (std::vector::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + { + writer.startRecord(ESM::REC_PROJ); + + ESM::ProjectileState state; + state.mId = it->mId; + state.mPosition = it->mNode->getPosition(); + state.mOrientation = it->mNode->getOrientation(); + state.mActorId = it->mActorId; + + state.mBowId = it->mBowId; + state.mVelocity = it->mVelocity; + + state.save(writer); + + writer.endRecord(ESM::REC_PROJ); + + progress.increaseProgress(); + } + + for (std::vector::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) + { + writer.startRecord(ESM::REC_MPRJ); + + ESM::MagicBoltState state; + state.mId = it->mId; + state.mPosition = it->mNode->getPosition(); + state.mOrientation = it->mNode->getOrientation(); + state.mActorId = it->mActorId; + + state.mSpellId = it->mSpellId; + state.mEffects = it->mEffects; + state.mSound = it->mSoundId; + state.mSourceName = it->mSourceName; + state.mSpeed = it->mSpeed; + state.mStack = it->mStack; + + state.save(writer); + + writer.endRecord(ESM::REC_MPRJ); + + progress.increaseProgress(); + } + } + + bool ProjectileManager::readRecord(ESM::ESMReader &reader, int32_t type) + { + if (type == ESM::REC_PROJ) + { + ESM::ProjectileState esm; + esm.load(reader); + + ProjectileState state; + state.mActorId = esm.mActorId; + state.mBowId = esm.mBowId; + state.mVelocity = esm.mVelocity; + state.mId = esm.mId; + + std::string model; + try + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); + MWWorld::Ptr ptr = ref.getPtr(); + model = ptr.getClass().getModel(ptr); + } + catch(...) + { + return true; + } + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation); + createModel(state, model); + + mProjectiles.push_back(state); + return true; + } + else if (type == ESM::REC_MPRJ) + { + ESM::MagicBoltState esm; + esm.load(reader); + + MagicBoltState state; + state.mSourceName = esm.mSourceName; + state.mId = esm.mId; + state.mSpellId = esm.mSpellId; + state.mActorId = esm.mActorId; + state.mSpeed = esm.mSpeed; + state.mStack = esm.mStack; + state.mEffects = esm.mEffects; + + std::string model; + try + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); + MWWorld::Ptr ptr = ref.getPtr(); + model = ptr.getClass().getModel(ptr); + } + catch(...) + { + return true; + } + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation); + createModel(state, model); + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f, + MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + + mMagicBolts.push_back(state); + return true; + } + + return false; + } + + int ProjectileManager::countSavedGameRecords() const + { + return mMagicBolts.size() + mProjectiles.size(); + } + } diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index c51985a7e..da965a4cf 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -20,6 +20,11 @@ namespace Physic } } +namespace Loading +{ + class Listener; +} + namespace Ogre { class SceneManager; @@ -46,6 +51,10 @@ namespace MWWorld /// Removes all current projectiles. Should be called when switching to a new worldspace. void clear(); + void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; + bool readRecord (ESM::ESMReader& reader, int32_t type); + int countSavedGameRecords() const; + private: OEngine::Physic::PhysicEngine& mPhysEngine; Ogre::SceneManager* mSceneMgr; @@ -54,15 +63,17 @@ namespace MWWorld { NifOgre::ObjectScenePtr mObject; Ogre::SceneNode* mNode; + + // Actor who shot this projectile + int mActorId; + + // MW-id of this projectile + std::string mId; }; struct MagicBoltState : public State { - // Id of spell or enchantment to apply when it hits - std::string mId; - - // Actor who casted this projectile - int mActorId; + std::string mSpellId; // Name of item to display as effect source in magic menu (in case we casted an enchantment) std::string mSourceName; @@ -74,16 +85,11 @@ namespace MWWorld bool mStack; MWBase::SoundPtr mSound; + std::string mSoundId; }; struct ProjectileState : public State { - // Actor who shot this projectile - int mActorId; - - // RefID of the projectile - std::string mProjectileId; - // RefID of the bow or crossbow the actor was using when this projectile was fired (may be empty) std::string mBowId; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 587fb71f4..1f645fd86 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -237,6 +237,8 @@ namespace MWWorld { mRendering->clear(); + mProjectileManager->clear(); + mLocalScripts.clear(); mPlayer->clear(); @@ -275,6 +277,7 @@ namespace MWWorld mCells.countSavedGameRecords() +mStore.countSavedGameRecords() +mGlobalVariables.countSavedGameRecords() + +mProjectileManager->countSavedGameRecords() +1 // player record +1 // weather record +1; // actorId counter @@ -298,6 +301,7 @@ namespace MWWorld mGlobalVariables.write (writer, progress); mPlayer->write (writer, progress); mWeatherManager->write (writer, progress); + mProjectileManager->write (writer, progress); } void World::readRecord (ESM::ESMReader& reader, int32_t type, @@ -313,7 +317,8 @@ namespace MWWorld !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && !mWeatherManager->readRecord (reader, type) && - !mCells.readRecord (reader, type, contentFileMap)) + !mCells.readRecord (reader, type, contentFileMap) && + !mProjectileManager->readRecord (reader, type)) { throw std::runtime_error ("unknown record in saved game"); } @@ -808,9 +813,13 @@ namespace MWWorld void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { - // changed worldspace - mProjectileManager->clear(); - mRendering->switchToInterior(); + if (mCurrentWorldSpace != cellName) + { + // changed worldspace + mProjectileManager->clear(); + mRendering->notifyWorldSpaceChanged(); + mCurrentWorldSpace = cellName; + } removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position); @@ -819,19 +828,22 @@ namespace MWWorld void World::changeToExteriorCell (const ESM::Position& position) { - if (!getPlayerPtr().getCell()->getCell()->isExterior()) + if (mCurrentWorldSpace != "sys::default") // FIXME { // changed worldspace mProjectileManager->clear(); - mRendering->switchToExterior(); + mRendering->notifyWorldSpaceChanged(); } removeContainerScripts(getPlayerPtr()); mWorldScene->changeToExteriorCell(position); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } - void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position) + void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange) { + if (!detectWorldSpaceChange) + mCurrentWorldSpace = cellId.mWorldspace; + if (cellId.mPaged) changeToExteriorCell (position); else diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8d2f3a9da..285d5fef6 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -75,6 +75,8 @@ namespace MWWorld Cells mCells; + std::string mCurrentWorldSpace; + OEngine::Physic::PhysicEngine* mPhysEngine; boost::shared_ptr mProjectileManager; @@ -311,7 +313,8 @@ namespace MWWorld virtual void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. - virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position); + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true); + ///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes virtual const ESM::Cell *getExterior (const std::string& cellName) const; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index c7deeadf3..e0166138e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -41,7 +41,7 @@ add_component_dir (esm loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate - npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate + npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 6c388de08..bdeb95291 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -109,6 +109,8 @@ enum RecNameInts REC_DYNA = FourCC<'D','Y','N','A'>::value, REC_ASPL = FourCC<'A','S','P','L'>::value, REC_ACTC = FourCC<'A','C','T','C'>::value, + REC_MPRJ = FourCC<'M','P','R','J'>::value, + REC_PROJ = FourCC<'P','R','O','J'>::value, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/projectilestate.cpp b/components/esm/projectilestate.cpp new file mode 100644 index 000000000..85c00025b --- /dev/null +++ b/components/esm/projectilestate.cpp @@ -0,0 +1,65 @@ +#include "projectilestate.hpp" + +#include "esmwriter.hpp" +#include "esmreader.hpp" + +namespace ESM +{ + + void BaseProjectileState::save(ESMWriter &esm) const + { + esm.writeHNString ("ID__", mId); + esm.writeHNT ("VEC3", mPosition); + esm.writeHNT ("QUAT", mOrientation); + esm.writeHNT ("ACTO", mActorId); + } + + void BaseProjectileState::load(ESMReader &esm) + { + mId = esm.getHNString("ID__"); + esm.getHNT (mPosition, "VEC3"); + esm.getHNT (mOrientation, "QUAT"); + esm.getHNT (mActorId, "ACTO"); + } + + void MagicBoltState::save(ESMWriter &esm) const + { + BaseProjectileState::save(esm); + + esm.writeHNString ("SPEL", mSpellId); + esm.writeHNString ("SRCN", mSourceName); + mEffects.save(esm); + esm.writeHNT ("SPED", mSpeed); + esm.writeHNT ("STCK", mStack); + esm.writeHNString ("SOUN", mSound); + } + + void MagicBoltState::load(ESMReader &esm) + { + BaseProjectileState::load(esm); + + mSpellId = esm.getHNString("SPEL"); + mSourceName = esm.getHNString ("SRCN"); + mEffects.load(esm); + esm.getHNT (mSpeed, "SPED"); + esm.getHNT (mStack, "STCK"); + mSound = esm.getHNString ("SOUN"); + } + + void ProjectileState::save(ESMWriter &esm) const + { + BaseProjectileState::save(esm); + + esm.writeHNString ("BOW_", mBowId); + esm.writeHNT ("VEL_", mVelocity); + } + + void ProjectileState::load(ESMReader &esm) + { + BaseProjectileState::load(esm); + + mBowId = esm.getHNString ("BOW_"); + esm.getHNT (mVelocity, "VEL_"); + } + +} diff --git a/components/esm/projectilestate.hpp b/components/esm/projectilestate.hpp new file mode 100644 index 000000000..6e36efb5b --- /dev/null +++ b/components/esm/projectilestate.hpp @@ -0,0 +1,90 @@ +#ifndef OPENMW_ESM_PROJECTILESTATE_H +#define OPENMW_ESM_PROJECTILESTATE_H + +#include + +#include +#include + +#include "effectlist.hpp" + +namespace ESM +{ + + // format 0, savegames only + + struct Quaternion + { + float mValues[4]; + + Quaternion() {} + Quaternion (Ogre::Quaternion q) + { + mValues[0] = q.w; + mValues[1] = q.x; + mValues[2] = q.y; + mValues[3] = q.z; + } + + operator Ogre::Quaternion () const + { + return Ogre::Quaternion(mValues[0], mValues[1], mValues[2], mValues[3]); + } + }; + + struct Vector3 + { + float mValues[3]; + + Vector3() {} + Vector3 (Ogre::Vector3 v) + { + mValues[0] = v.x; + mValues[1] = v.y; + mValues[2] = v.z; + } + + operator Ogre::Vector3 () const + { + return Ogre::Vector3(&mValues[0]); + } + }; + + struct BaseProjectileState + { + std::string mId; + + Vector3 mPosition; + Quaternion mOrientation; + + int mActorId; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + + struct MagicBoltState : public BaseProjectileState + { + std::string mSpellId; + std::string mSourceName; + ESM::EffectList mEffects; + float mSpeed; + bool mStack; + std::string mSound; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + + struct ProjectileState : public BaseProjectileState + { + std::string mBowId; + Vector3 mVelocity; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + +} + +#endif From 92c5bb56e08bd30e790f69a46eb7e01611201111 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 May 2014 05:34:54 +0200 Subject: [PATCH 12/13] Un-reverted actorId for AiPursue --- apps/openmw/mwmechanics/actors.cpp | 4 ++-- apps/openmw/mwmechanics/aipursue.cpp | 11 ++++++----- apps/openmw/mwmechanics/aipursue.hpp | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d54805539..a3211892e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -759,7 +759,7 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); + creatureStats.getAiSequence().stack(AiPursue(player), ptr); creatureStats.setAlarmed(true); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); } @@ -788,7 +788,7 @@ namespace MWMechanics else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue) { if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); + creatureStats.getAiSequence().stack(AiPursue(player), ptr); else { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 6e91ccb71..aa71af465 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -7,12 +7,14 @@ #include "../mwworld/action.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "steering.hpp" #include "movement.hpp" #include "creaturestats.hpp" -MWMechanics::AiPursue::AiPursue(const std::string &objectId) - : mObjectId(objectId) +MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr& actor) + : mActorId(actor.getClass().getCreatureStats(actor).getActorId()) { } MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const @@ -23,7 +25,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) { ESM::Position pos = actor.getRefData().getPosition(); //position of the actor - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist @@ -33,8 +35,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); - MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player + target.getClass().activate(target,actor).get()->execute(actor); //Arrest player return true; } else { diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 2eb533d62..90ff8df93 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -16,14 +16,14 @@ namespace MWMechanics { public: ///Constructor - /** \param objectId Actor to pursue **/ - AiPursue(const std::string &objectId); + /** \param actor Actor to pursue **/ + AiPursue(const MWWorld::Ptr& actor); virtual AiPursue *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual int getTypeId() const; private: - std::string mObjectId; + int mActorId; // The actor to pursue int mCellX; int mCellY; }; From ae66d28c87d9331f0a42aca806c46232b9a6aa06 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 May 2014 09:05:41 +0200 Subject: [PATCH 13/13] Feature #32: Implement respawn for containers, creatures and NPCs --- apps/openmw/mwclass/container.cpp | 10 +++++++ apps/openmw/mwclass/container.hpp | 2 ++ apps/openmw/mwclass/creature.cpp | 20 ++++++++++++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/creaturelevlist.cpp | 27 ++++++++++++++++--- apps/openmw/mwclass/creaturelevlist.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 20 ++++++++++++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwworld/cellstore.cpp | 36 ++++++++++++++++++++++++- apps/openmw/mwworld/cellstore.hpp | 7 +++++ apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/scene.cpp | 2 ++ components/esm/cellstate.cpp | 8 +++++- components/esm/cellstate.hpp | 4 +++ components/esm/creaturelevliststate.cpp | 6 +++++ components/esm/creaturelevliststate.hpp | 1 + 16 files changed, 146 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index be76bd0b4..f0c71bf53 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -60,6 +60,16 @@ namespace MWClass } } + void Container::respawn(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + if (ref->mBase->mFlags & ESM::Container::Respawn) + { + ptr.getRefData().setCustomData(NULL); + } + } + void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index f012d675c..79a801248 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -64,6 +64,8 @@ namespace MWClass static void registerSelf(); + virtual void respawn (const MWWorld::Ptr& ptr) const; + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index dc23b63f3..4abc9ea71 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -822,6 +822,26 @@ namespace MWClass return ptr.get()->mBase->mData.mGold; } + void Creature::respawn(const MWWorld::Ptr &ptr) const + { + if (ptr.get()->mBase->mFlags & ESM::Creature::Respawn) + { + // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. + // This also means we cannot respawn dynamically placed references with no content file connection. + if (ptr.getCellRef().mRefNum.mContentFile != -1) + { + if (ptr.getRefData().getCount() == 0) + ptr.getRefData().setCount(1); + + // Reset to original position + ESM::Position& pos = ptr.getRefData().getPosition(); + pos = ptr.getCellRef().mPos; + + ptr.getRefData().setCustomData(NULL); + } + } + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting *Creature::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 04c010c83..d3ffb321e 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -143,6 +143,8 @@ namespace MWClass ///< Write additional state from \a ptr into \a state. virtual int getBaseGold(const MWWorld::Ptr& ptr) const; + + virtual void respawn (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index be01b848a..fea30735c 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -14,6 +14,7 @@ namespace { // actorId of the creature we spawned int mSpawnActorId; + bool mSpawn; // Should a new creature be spawned? virtual MWWorld::CustomData *clone() const; }; @@ -31,6 +32,14 @@ namespace MWClass return ""; } + void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const + { + ensureCustomData(ptr); + + CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + customData.mSpawn = true; + } + void CreatureLevList::registerSelf() { boost::shared_ptr instance (new CreatureLevList); @@ -43,9 +52,8 @@ namespace MWClass ensureCustomData(ptr); CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); - if (customData.mSpawnActorId != -1) - return; // TODO: handle respawning - + if (!customData.mSpawn) + return; MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,11 +62,21 @@ namespace MWClass if (!id.empty()) { + // Delete the previous creature + if (customData.mSpawnActorId != -1) + { + MWWorld::Ptr creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId); + if (!creature.isEmpty()) + MWBase::Environment::get().getWorld()->deleteObject(creature); + customData.mSpawnActorId = -1; + } + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); MWWorld::ManualRef ref(store, id); ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos); customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); + customData.mSpawn = false; } } @@ -68,6 +86,7 @@ namespace MWClass { std::auto_ptr data (new CreatureLevListCustomData); data->mSpawnActorId = -1; + data->mSpawn = true; ptr.getRefData().setCustomData(data.release()); } @@ -81,6 +100,7 @@ namespace MWClass ensureCustomData(ptr); CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); customData.mSpawnActorId = state2.mSpawnActorId; + customData.mSpawn = state2.mSpawn; } void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -91,5 +111,6 @@ namespace MWClass ensureCustomData(ptr); CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); state2.mSpawnActorId = customData.mSpawnActorId; + state2.mSpawn = customData.mSpawn; } } diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index b67fb5523..6c51a3189 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -27,6 +27,8 @@ namespace MWClass virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. + + virtual void respawn (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6d2eea6af..5b9e26f26 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1310,6 +1310,26 @@ namespace MWClass return Misc::StringUtils::ciEqual(ptr.get()->mBase->mClass, className); } + void Npc::respawn(const MWWorld::Ptr &ptr) const + { + if (ptr.get()->mBase->mFlags & ESM::NPC::Respawn) + { + // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. + // This also means we cannot respawn dynamically placed references with no content file connection. + if (ptr.getCellRef().mRefNum.mContentFile != -1) + { + if (ptr.getRefData().getCount() == 0) + ptr.getRefData().setCount(1); + + // Reset to original position + ESM::Position& pos = ptr.getRefData().getPosition(); + pos = ptr.getCellRef().mPos; + + ptr.getRefData().setCustomData(NULL); + } + } + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 4b9c8988e..03c6e234a 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -178,6 +178,8 @@ namespace MWClass virtual bool canWalk (const MWWorld::Ptr &ptr) const { return true; } + + virtual void respawn (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 5d2d8a517..aa247f0b9 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -169,7 +169,7 @@ namespace MWWorld } CellStore::CellStore (const ESM::Cell *cell) - : mCell (cell), mState (State_Unloaded), mHasState (false) + : mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) { mWaterLevel = cell->mWater; } @@ -555,6 +555,7 @@ namespace MWWorld mWaterLevel = state.mWaterLevel; mWaterLevel = state.mWaterLevel; + mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn); } void CellStore::saveState (ESM::CellState& state) const @@ -566,6 +567,7 @@ namespace MWWorld state.mWaterLevel = mWaterLevel; state.mHasFogOfWar = (mFogState.get() ? 1 : 0); + state.mLastRespawn = mLastRespawn.toEsm(); } void CellStore::writeFog(ESM::ESMWriter &writer) const @@ -754,4 +756,36 @@ namespace MWWorld { return mFogState.get(); } + + void CellStore::respawn() + { + if (mState == State_Loaded) + { + static int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->getInt(); + if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn) + { + mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); + for (CellRefList::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + for (CellRefList::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + } + } + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 91c536b43..ba6fad7ba 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -13,6 +13,8 @@ #include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld +#include "timestamp.hpp" + namespace ESM { struct CellState; @@ -48,6 +50,8 @@ namespace MWWorld std::vector mIds; float mWaterLevel; + MWWorld::TimeStamp mLastRespawn; + CellRefList mActivators; CellRefList mPotions; CellRefList mAppas; @@ -161,6 +165,9 @@ namespace MWWorld void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + void respawn (); + ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. + template CellRefList& get() { throw std::runtime_error ("Storage for this type not exist in cells"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 057bc906e..f47b8ace3 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -341,6 +341,8 @@ namespace MWWorld virtual int getDoorState (const MWWorld::Ptr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; + + virtual void respawn (const MWWorld::Ptr& ptr) const {} }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ecc8bdd02..25ee0c2e8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -165,6 +165,8 @@ namespace MWWorld } } + cell->respawn(); + // ... then references. This is important for adjustPosition to work correctly. /// \todo rescale depending on the state of a new GMST insertCell (*cell, true, loadingListener); diff --git a/components/esm/cellstate.cpp b/components/esm/cellstate.cpp index 541a359c6..4df04d0e5 100644 --- a/components/esm/cellstate.cpp +++ b/components/esm/cellstate.cpp @@ -11,6 +11,10 @@ void ESM::CellState::load (ESMReader &esm) mHasFogOfWar = false; esm.getHNOT (mHasFogOfWar, "HFOW"); + + mLastRespawn.mDay = 0; + mLastRespawn.mHour = 0; + esm.getHNOT (mLastRespawn, "RESP"); } void ESM::CellState::save (ESMWriter &esm) const @@ -18,5 +22,7 @@ void ESM::CellState::save (ESMWriter &esm) const if (!mId.mPaged) esm.writeHNT ("WLVL", mWaterLevel); - esm.writeHNT("HFOW", mHasFogOfWar); + esm.writeHNT ("HFOW", mHasFogOfWar); + + esm.writeHNT ("RESP", mLastRespawn); } diff --git a/components/esm/cellstate.hpp b/components/esm/cellstate.hpp index 88918a3ab..55c1e5155 100644 --- a/components/esm/cellstate.hpp +++ b/components/esm/cellstate.hpp @@ -3,6 +3,8 @@ #include "cellid.hpp" +#include "defs.hpp" + namespace ESM { class ESMReader; @@ -19,6 +21,8 @@ namespace ESM int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp) + ESM::TimeStamp mLastRespawn; + void load (ESMReader &esm); void save (ESMWriter &esm) const; }; diff --git a/components/esm/creaturelevliststate.cpp b/components/esm/creaturelevliststate.cpp index 164dae96e..21cc73b89 100644 --- a/components/esm/creaturelevliststate.cpp +++ b/components/esm/creaturelevliststate.cpp @@ -12,6 +12,9 @@ namespace ESM mSpawnActorId = -1; esm.getHNOT (mSpawnActorId, "SPAW"); + + mSpawn = false; + esm.getHNOT (mSpawn, "RESP"); } void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const @@ -20,6 +23,9 @@ namespace ESM if (mSpawnActorId != -1) esm.writeHNT ("SPAW", mSpawnActorId); + + if (mSpawn) + esm.writeHNT ("RESP", mSpawn); } } diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp index 99b5a7fa2..da64cd7c2 100644 --- a/components/esm/creaturelevliststate.hpp +++ b/components/esm/creaturelevliststate.hpp @@ -10,6 +10,7 @@ namespace ESM struct CreatureLevListState : public ObjectState { int mSpawnActorId; + bool mSpawn; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const;