From 86f691d3d59023d9c3fdef0550bd2a08282f0df6 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 22:48:24 -0800 Subject: [PATCH 01/16] split MWWord::World::update into multiple functions --- apps/openmw/mwworld/worldimp.cpp | 258 +++++++++++++++++-------------- apps/openmw/mwworld/worldimp.hpp | 7 + 2 files changed, 152 insertions(+), 113 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4f60f6ef42..26614f3e13 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -884,8 +884,6 @@ namespace MWWorld void World::update (float duration, bool paused) { - /// \todo split this function up into subfunctions - mWorldScene->update (duration, paused); float pitch, yaw; @@ -895,6 +893,13 @@ namespace MWWorld mWeatherManager->update (duration); + performUpdateSceneQueries (); + + updateWindowManager (); + } + + void World::updateWindowManager () + { // inform the GUI about focused object MWWorld::Ptr object = searchPtrViaHandle(mFacedHandle); @@ -918,7 +923,10 @@ namespace MWWorld screenCoords[0], screenCoords[1], screenCoords[2], screenCoords[3]); } } + } + void World::performUpdateSceneQueries () + { if (!mRendering->occlusionQuerySupported()) { // cast a ray from player to sun to detect if the sun is visible @@ -937,121 +945,145 @@ namespace MWWorld MWRender::OcclusionQuery* query = mRendering->getOcclusionQuery(); if (!query->occlusionTestPending()) { - // get result of last query - if (mNumFacing == 0) mFacedHandle = ""; - else if (mNumFacing == 1) - { - bool result = query->getTestResult(); - mFacedHandle = result ? mFaced1Name : ""; - } - else if (mNumFacing == 2) - { - bool result = query->getTestResult(); - mFacedHandle = result ? mFaced2Name : mFaced1Name; - } - - // send new query - // figure out which object we want to test against - std::vector < std::pair < float, std::string > > results; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedObjects(x, y); - } - else - results = mPhysics->getFacedObjects(); - - // ignore the player and other things we're not interested in - std::vector < std::pair < float, std::string > >::iterator it = results.begin(); - while (it != results.end()) - { - if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain - || getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) - { - it = results.erase(it); - } - else - ++it; - } - - if (results.size() == 0) - { - mNumFacing = 0; - } - else if (results.size() == 1) - { - mFaced1 = getPtrViaHandle(results.front().second); - mFaced1Name = results.front().second; - mNumFacing = 1; - - btVector3 p; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - p = mPhysics->getRayPoint(results.front().first, x, y); - } - else - p = mPhysics->getRayPoint(results.front().first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); - Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode(); - - //std::cout << "Num facing 1 : " << mFaced1Name << std::endl; - //std::cout << "Type 1 " << mFaced1.getTypeName() << std::endl; - - query->occlusionTest(pos, node); - } - else - { - mFaced1Name = results.front().second; - mFaced2Name = results[1].second; - mFaced1 = getPtrViaHandle(results.front().second); - mFaced2 = getPtrViaHandle(results[1].second); - mNumFacing = 2; - - btVector3 p; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - p = mPhysics->getRayPoint(results[1].first, x, y); - } - else - p = mPhysics->getRayPoint(results[1].first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); - Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode(); - Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode(); - - // no need to test if the first node is not occluder - if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos)) - { - mFacedHandle = mFaced1Name; - //std::cout << "node1 Not an occluder" << std::endl; - return; - } - - // no need to test if the second object is static (thus cannot be activated) - if (mFaced2.getTypeName().find("Static") != std::string::npos) - { - mFacedHandle = mFaced1Name; - return; - } - - // work around door problems - if (mFaced1.getTypeName().find("Static") != std::string::npos - && mFaced2.getTypeName().find("Door") != std::string::npos) - { - mFacedHandle = mFaced2Name; - return; - } - - query->occlusionTest(pos, node2); - } + processFacedQueryResults (query); + beginFacedQueryProcess (query); } } } + void World::processFacedQueryResults (MWRender::OcclusionQuery* query) + { + // get result of last query + if (mNumFacing == 0) + { + mFacedHandle = ""; + } + else if (mNumFacing == 1) + { + bool result = query->getTestResult(); + mFacedHandle = result ? mFaced1Name : ""; + } + else if (mNumFacing == 2) + { + bool result = query->getTestResult(); + mFacedHandle = result ? mFaced2Name : mFaced1Name; + } + } + + void World::beginFacedQueryProcess (MWRender::OcclusionQuery* query) + { + // send new query + // figure out which object we want to test against + std::vector < std::pair < float, std::string > > results; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + float x, y; + MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); + results = mPhysics->getFacedObjects(x, y); + } + else + { + results = mPhysics->getFacedObjects(); + } + + // ignore the player and other things we're not interested in + std::vector < std::pair < float, std::string > >::iterator it = results.begin(); + while (it != results.end()) + { + if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain + || getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) + { + it = results.erase(it); + } + else + ++it; + } + + if (results.size() == 0) + { + mNumFacing = 0; + } + else if (results.size() == 1) + { + beginSingleFacedQueryProcess (query, results); + } + else + { + beginDoubleFacedQueryProcess (query, results); + } + } + + void World::beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results) + { + mFaced1 = getPtrViaHandle(results.front().second); + mFaced1Name = results.front().second; + mNumFacing = 1; + + btVector3 p; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + float x, y; + MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); + p = mPhysics->getRayPoint(results.front().first, x, y); + } + else + p = mPhysics->getRayPoint(results.front().first); + Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode(); + + //std::cout << "Num facing 1 : " << mFaced1Name << std::endl; + //std::cout << "Type 1 " << mFaced1.getTypeName() << std::endl; + + query->occlusionTest(pos, node); + } + + void World::beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results) + { + mFaced1Name = results.at (0).second; + mFaced2Name = results.at (1).second; + mFaced1 = getPtrViaHandle(results.at (0).second); + mFaced2 = getPtrViaHandle(results.at (1).second); + mNumFacing = 2; + + btVector3 p; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + float x, y; + MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); + p = mPhysics->getRayPoint(results.at (1).first, x, y); + } + else + p = mPhysics->getRayPoint(results.at (1).first); + Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode(); + Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode(); + + // no need to test if the first node is not occluder + if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos)) + { + mFacedHandle = mFaced1Name; + //std::cout << "node1 Not an occluder" << std::endl; + return; + } + + // no need to test if the second object is static (thus cannot be activated) + if (mFaced2.getTypeName().find("Static") != std::string::npos) + { + mFacedHandle = mFaced1Name; + return; + } + + // work around door problems + if (mFaced1.getTypeName().find("Static") != std::string::npos + && mFaced2.getTypeName().find("Door") != std::string::npos) + { + mFacedHandle = mFaced2Name; + return; + } + + query->occlusionTest(pos, node2); + } + bool World::isCellExterior() const { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d29adebf44..a60332e265 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -90,6 +90,13 @@ namespace MWWorld virtual void copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + void updateWindowManager (); + void performUpdateSceneQueries (); + void processFacedQueryResults (MWRender::OcclusionQuery* query); + void beginFacedQueryProcess (MWRender::OcclusionQuery* query); + void beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); + void beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); + public: World (OEngine::Render::OgreRenderer& renderer, From 0108be2e4f4e2e9257622667d3ecbf1666a29e1e Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:00:00 -0800 Subject: [PATCH 02/16] updated MWWorld::PhysicsSystem::getFacedXXX functions * changed the names and return values to be consistent * made the distance to search a parameter * change the distance returned to world units instead of percentage of query distance --- apps/openmw/mwworld/physicssystem.cpp | 35 ++++++++++++++++++--------- apps/openmw/mwworld/physicssystem.hpp | 7 +++--- apps/openmw/mwworld/worldimp.cpp | 12 ++++----- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 5359c4eb27..9e2e94143f 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -43,7 +43,7 @@ namespace MWWorld return mEngine; } - std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) { btVector3 dir(0, 1, 0); dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); @@ -56,11 +56,14 @@ namespace MWWorld mPlayerData.eyepos.z); origin += dir * 5; - btVector3 dest = origin + dir * 500; - return mEngine->rayTest(origin, dest); + btVector3 dest = origin + dir * queryDistance; + std::pair result; + /*auto*/ result = mEngine->rayTest(origin, dest); + result.second *= queryDistance; + return std::make_pair (result.second, result.first); } - std::vector < std::pair > PhysicsSystem::getFacedObjects () + std::vector < std::pair > PhysicsSystem::getFacedHandles (float queryDistance) { btVector3 dir(0, 1, 0); dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); @@ -73,22 +76,32 @@ namespace MWWorld mPlayerData.eyepos.z); origin += dir * 5; - btVector3 dest = origin + dir * 500; - return mEngine->rayTest2(origin, dest); + btVector3 dest = origin + dir * queryDistance; + std::vector < std::pair > results; + /* auto */ results = mEngine->rayTest2(origin, dest); + std::vector < std::pair >::iterator i; + for (/* auto */ i = results.begin (); i != results.end (); ++i) + i->first *= queryDistance; + return results; } - std::vector < std::pair > PhysicsSystem::getFacedObjects (float mouseX, float mouseY) + std::vector < std::pair > PhysicsSystem::getFacedHandles (float mouseX, float mouseY, float queryDistance) { Ray ray = mRender.getCamera()->getCameraToViewportRay(mouseX, mouseY); Ogre::Vector3 from = ray.getOrigin(); - Ogre::Vector3 to = ray.getPoint(500); /// \todo make this distance (ray length) configurable + Ogre::Vector3 to = ray.getPoint(queryDistance); btVector3 _from, _to; // OGRE to MW coordinates _from = btVector3(from.x, -from.z, from.y); _to = btVector3(to.x, -to.z, to.y); - return mEngine->rayTest2(_from,_to); + std::vector < std::pair > results; + /* auto */ results = mEngine->rayTest2(_from,_to); + std::vector < std::pair >::iterator i; + for (/* auto */ i = results.begin (); i != results.end (); ++i) + i->first *= queryDistance; + return results; } void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight) @@ -110,7 +123,7 @@ namespace MWWorld Ray centerRay = mRender.getCamera()->getCameraToViewportRay( mRender.getViewport()->getWidth()/2, mRender.getViewport()->getHeight()/2); - btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y); /// \todo make this distance (ray length) configurable + btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); return result; } @@ -118,7 +131,7 @@ namespace MWWorld { //get a ray pointing to the center of the viewport Ray centerRay = mRender.getCamera()->getCameraToViewportRay(mouseX, mouseY); - btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y); /// \todo make this distance (ray length) configurable + btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); return result; } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 8bd73fd6cb..81715c31e9 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -41,14 +41,13 @@ namespace MWWorld bool toggleCollisionMode(); - std::pair getFacedHandle (MWWorld::World& world); + std::pair getFacedHandle (MWWorld::World& world, float queryDistance); + std::vector < std::pair > getFacedHandles (float queryDistance); + std::vector < std::pair > getFacedHandles (float mouseX, float mouseY, float queryDistance); btVector3 getRayPoint(float extent); btVector3 getRayPoint(float extent, float mouseX, float mouseY); - std::vector < std::pair > getFacedObjects (); - - std::vector < std::pair > getFacedObjects (float mouseX, float mouseY); // cast ray, return true if it hit something bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 26614f3e13..09c48bd137 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -577,13 +577,13 @@ namespace MWWorld { if (!mRendering->occlusionQuerySupported()) { - std::pair result = mPhysics->getFacedHandle (*this); + std::pair result = mPhysics->getFacedHandle (*this, 500); - if (result.first.empty() || - result.second>getStore().get().find ("iMaxActivateDist")->getInt()) + if (result.second.empty() || + result.first>getStore().get().find ("iMaxActivateDist")->getInt()) return ""; - return result.first; + return result.second; } else { @@ -979,11 +979,11 @@ namespace MWWorld { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedObjects(x, y); + results = mPhysics->getFacedHandles(x, y, 500); } else { - results = mPhysics->getFacedObjects(); + results = mPhysics->getFacedHandles(500); } // ignore the player and other things we're not interested in From 05dad29005c9651829c7695cc3da8a6bf4b2ee68 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:11:15 -0800 Subject: [PATCH 03/16] update MWWord::World to track distances while performing occlusion queries. This allows checking the activation distance against what is stored in GameSettings. Experimentation suggests that a second distance is required for NPCs. --- apps/openmw/mwworld/worldimp.cpp | 12 ++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 09c48bd137..002ffcd935 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -587,6 +587,9 @@ namespace MWWorld } else { + if (mFacedDistance > result.first>getStore().get().find ("iMaxActivateDist")->getInt()) + return ""; + // updated every few frames in update() return mFacedHandle; } @@ -957,16 +960,19 @@ namespace MWWorld if (mNumFacing == 0) { mFacedHandle = ""; + mFacedDistance = FLT_MAX; } else if (mNumFacing == 1) { bool result = query->getTestResult(); mFacedHandle = result ? mFaced1Name : ""; + mFacedDistance = result ? mFaced1Distance : FLT_MAX; } else if (mNumFacing == 2) { bool result = query->getTestResult(); mFacedHandle = result ? mFaced2Name : mFaced1Name; + mFacedDistance = result ? mFaced1Distance : mFaced1Distance; } } @@ -1017,6 +1023,7 @@ namespace MWWorld { mFaced1 = getPtrViaHandle(results.front().second); mFaced1Name = results.front().second; + mFaced1Distance = results.front().first; mNumFacing = 1; btVector3 p; @@ -1041,6 +1048,8 @@ namespace MWWorld { mFaced1Name = results.at (0).second; mFaced2Name = results.at (1).second; + mFaced1Distance = results.at (0).first; + mFaced2Distance = results.at (1).first; mFaced1 = getPtrViaHandle(results.at (0).second); mFaced2 = getPtrViaHandle(results.at (1).second); mNumFacing = 2; @@ -1062,6 +1071,7 @@ namespace MWWorld if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos)) { mFacedHandle = mFaced1Name; + mFacedDistance = mFaced1Distance; //std::cout << "node1 Not an occluder" << std::endl; return; } @@ -1070,6 +1080,7 @@ namespace MWWorld if (mFaced2.getTypeName().find("Static") != std::string::npos) { mFacedHandle = mFaced1Name; + mFacedDistance = mFaced1Distance; return; } @@ -1078,6 +1089,7 @@ namespace MWWorld && mFaced2.getTypeName().find("Door") != std::string::npos) { mFacedHandle = mFaced2Name; + mFacedDistance = mFaced2Distance; return; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a60332e265..de6689debc 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -72,10 +72,13 @@ namespace MWWorld Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); std::string mFacedHandle; + float mFacedDistance; Ptr mFaced1; Ptr mFaced2; std::string mFaced1Name; std::string mFaced2Name; + float mFaced1Distance; + float mFaced2Distance; int mNumFacing; std::map mFallback; From b9fbd6ae4b4be8319eb31596a6aa77e598cd90ae Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:27:37 -0800 Subject: [PATCH 04/16] Factored faced object lookups into MWRender::World * Renamed MWWorld::World::getFacedHandle to getFacedObject. * Changed it to return an object pointer * Updated clients to use return object directly. --- apps/openmw/engine.cpp | 7 +------ apps/openmw/mwbase/world.hpp | 4 ++-- apps/openmw/mwgui/hud.cpp | 3 +-- apps/openmw/mwgui/tooltips.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 32 +++++++++++++++----------------- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 6 files changed, 22 insertions(+), 31 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index aadeb7f3a2..fcfaed47dd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -427,12 +427,7 @@ void OMW::Engine::activate() if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); - - if (handle.empty()) - return; - - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle (handle); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); if (ptr.isEmpty()) return; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index cc625b3067..6f7b6e61a9 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -199,8 +199,8 @@ namespace MWBase virtual void markCellAsUnchanged() = 0; - virtual std::string getFacedHandle() = 0; - ///< Return handle of the object the player is looking at + virtual MWWorld::Ptr getFacedObject() = 0; + ///< Return pointer to the object the player is looking at, if it is within activation range virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 689baf488f..5ea1d13388 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -244,8 +244,7 @@ void HUD::onWorldClicked(MyGUI::Widget* _sender) if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) return; - std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); - MWWorld::Ptr object = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle); + MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); if (mode == GM_Console) MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 97df211acc..c7acf568d1 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -80,9 +80,8 @@ void ToolTips::onFrame(float frameDuration) || (mWindowManager->getMode() == GM_Container) || (mWindowManager->getMode() == GM_Inventory))) { - std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); + mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); - mFocusObject = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle); if (mFocusObject.isEmpty ()) return; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 002ffcd935..a893a6776c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -573,26 +573,24 @@ namespace MWWorld return mWorldScene->markCellAsUnchanged(); } - std::string World::getFacedHandle() + MWWorld::Ptr World::getFacedObject() { + std::pair result; + if (!mRendering->occlusionQuerySupported()) - { - std::pair result = mPhysics->getFacedHandle (*this, 500); - - if (result.second.empty() || - result.first>getStore().get().find ("iMaxActivateDist")->getInt()) - return ""; - - return result.second; - } + result = mPhysics->getFacedHandle (*this, 500); else - { - if (mFacedDistance > result.first>getStore().get().find ("iMaxActivateDist")->getInt()) - return ""; + result = std::make_pair (mFacedDistance, mFacedHandle); - // updated every few frames in update() - return mFacedHandle; - } + if (result.second.empty()) + return MWWorld::Ptr (); + + float ActivationDistance = getStore().get().find ("iMaxActivateDist")->getInt(); + + if (result.first > ActivationDistance) + return MWWorld::Ptr (); + + return searchPtrViaHandle (result.second); } void World::deleteObject (const Ptr& ptr) @@ -904,7 +902,7 @@ namespace MWWorld void World::updateWindowManager () { // inform the GUI about focused object - MWWorld::Ptr object = searchPtrViaHandle(mFacedHandle); + MWWorld::Ptr object = getFacedObject (); MWBase::Environment::get().getWindowManager()->setFocusObject(object); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index de6689debc..62fb1d89af 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -227,8 +227,8 @@ namespace MWWorld virtual void markCellAsUnchanged(); - virtual std::string getFacedHandle(); - ///< Return handle of the object the player is looking at + virtual MWWorld::Ptr getFacedObject(); + ///< Return pointer to the object the player is looking at, if it is within activation range virtual void deleteObject (const Ptr& ptr); From b3932e3deae509f5d82647e308cc313ac17817e5 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:40:17 -0800 Subject: [PATCH 05/16] added a separate activation distance for NPCs --- apps/openmw/mwworld/worldimp.cpp | 32 +++++++++++++++++++++++++++----- apps/openmw/mwworld/worldimp.hpp | 4 ++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a893a6776c..739d0daab3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -573,24 +573,46 @@ namespace MWWorld return mWorldScene->markCellAsUnchanged(); } + float World::getMaxActivationDistance () + { + return (std::max) (getNpcActivationDistance (), getObjectActivationDistance ()); + } + + float World::getNpcActivationDistance () + { + return getStore().get().find ("iMaxActivateDist")->getInt()*5/4; + } + + float World::getObjectActivationDistance () + { + return getStore().get().find ("iMaxActivateDist")->getInt(); + } + MWWorld::Ptr World::getFacedObject() { std::pair result; if (!mRendering->occlusionQuerySupported()) - result = mPhysics->getFacedHandle (*this, 500); + result = mPhysics->getFacedHandle (*this, getMaxActivationDistance ()); else result = std::make_pair (mFacedDistance, mFacedHandle); if (result.second.empty()) return MWWorld::Ptr (); - float ActivationDistance = getStore().get().find ("iMaxActivateDist")->getInt(); + MWWorld::Ptr object = searchPtrViaHandle (result.second); + + float ActivationDistance; + + if (object.getTypeName ().find("NPC") != std::string::npos) + ActivationDistance = getNpcActivationDistance (); + else + ActivationDistance = getObjectActivationDistance (); if (result.first > ActivationDistance) return MWWorld::Ptr (); - return searchPtrViaHandle (result.second); + return object; } void World::deleteObject (const Ptr& ptr) @@ -983,11 +1005,11 @@ namespace MWWorld { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedHandles(x, y, 500); + results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()); } else { - results = mPhysics->getFacedHandles(500); + results = mPhysics->getFacedHandles(getMaxActivationDistance ()); } // ignore the player and other things we're not interested in diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 62fb1d89af..a1ba6fcdeb 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -100,6 +100,10 @@ namespace MWWorld void beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); void beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); + float getMaxActivationDistance (); + float getNpcActivationDistance (); + float getObjectActivationDistance (); + public: World (OEngine::Render::OgreRenderer& renderer, From e18cf452d4728adad7bdd2faad870399c7fe05b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 8 Jan 2013 13:24:40 +0100 Subject: [PATCH 06/16] Updated shiny again. Some recent changes were accidently overwritten by eduard on 12/30/2012 --- .gitmodules | 1 - extern/shiny/CMakeLists.txt | 3 + extern/shiny/Main/Factory.cpp | 2 +- extern/shiny/Main/MaterialInstance.cpp | 8 +- extern/shiny/Main/ScriptLoader.cpp | 110 ++++++++++++------------- extern/shiny/Main/ScriptLoader.hpp | 42 +++++----- 6 files changed, 85 insertions(+), 81 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8b13789179..0000000000 --- a/.gitmodules +++ /dev/null @@ -1 +0,0 @@ - diff --git a/extern/shiny/CMakeLists.txt b/extern/shiny/CMakeLists.txt index c27850ed65..603336413e 100644 --- a/extern/shiny/CMakeLists.txt +++ b/extern/shiny/CMakeLists.txt @@ -70,3 +70,6 @@ endif() link_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set(SHINY_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE) +set(SHINY_OGREPLATFORM_LIBRARY ${SHINY_OGREPLATFORM_LIBRARY} PARENT_SCOPE) diff --git a/extern/shiny/Main/Factory.cpp b/extern/shiny/Main/Factory.cpp index 678ee25c96..82d6648110 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -224,7 +224,7 @@ namespace sh if (!mShadersEnabled) newInstance.setShadersEnabled (false); - newInstance.setSourceFile (it->second->m_fileName); + newInstance.setSourceFile (it->second->mFileName); std::vector props = it->second->getChildren(); for (std::vector::const_iterator propIt = props.begin(); propIt != props.end(); ++propIt) diff --git a/extern/shiny/Main/MaterialInstance.cpp b/extern/shiny/Main/MaterialInstance.cpp index 3abc781f61..0f8bcdda78 100644 --- a/extern/shiny/Main/MaterialInstance.cpp +++ b/extern/shiny/Main/MaterialInstance.cpp @@ -72,6 +72,8 @@ namespace sh allowFixedFunction = retrieveValue(getProperty("allow_fixed_function"), NULL).get(); } + bool useShaders = mShadersEnabled || !allowFixedFunction; + // get passes of the top-most parent PassVector passes = getPasses(); if (passes.size() == 0) @@ -91,7 +93,7 @@ namespace sh // create or retrieve shaders bool hasVertex = it->hasProperty("vertex_program"); bool hasFragment = it->hasProperty("fragment_program"); - if (mShadersEnabled || !allowFixedFunction) + if (useShaders) { it->setContext(context); it->mShaderProperties.setContext(context); @@ -144,7 +146,7 @@ namespace sh bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end(); bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end(); if ( (foundVertex || foundFragment) - || (((!mShadersEnabled || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) + || (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) { boost::shared_ptr texUnit = pass->createTextureUnitState (); texIt->copyAll (texUnit.get(), context); @@ -152,7 +154,7 @@ namespace sh mTexUnits.push_back(texUnit); // set texture unit indices (required by GLSL) - if (mShadersEnabled && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) + if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) { pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); diff --git a/extern/shiny/Main/ScriptLoader.cpp b/extern/shiny/Main/ScriptLoader.cpp index a8971dc87d..93d728b023 100644 --- a/extern/shiny/Main/ScriptLoader.cpp +++ b/extern/shiny/Main/ScriptLoader.cpp @@ -14,9 +14,9 @@ namespace sh for ( boost::filesystem::recursive_directory_iterator end, dir(path); dir != end; ++dir ) { boost::filesystem::path p(*dir); - if(p.extension() == c->m_fileEnding) + if(p.extension() == c->mFileEnding) { - c->m_currentFileName = (*dir).path().string(); + c->mCurrentFileName = (*dir).path().string(); std::ifstream in((*dir).path().string().c_str(), std::ios::binary); c->parseScript(in); } @@ -25,7 +25,7 @@ namespace sh ScriptLoader::ScriptLoader(const std::string& fileEnding) { - m_fileEnding = fileEnding; + mFileEnding = fileEnding; } ScriptLoader::~ScriptLoader() @@ -70,7 +70,7 @@ namespace sh { //Get first token _nextToken(stream); - if (tok == TOKEN_EOF) + if (mToken == TOKEN_EOF) { stream.close(); return; @@ -87,7 +87,7 @@ namespace sh //EOF token if (!stream.good()) { - tok = TOKEN_EOF; + mToken = TOKEN_EOF; return; } @@ -101,7 +101,7 @@ namespace sh if (!stream.good()) { - tok = TOKEN_EOF; + mToken = TOKEN_EOF; return; } @@ -115,21 +115,21 @@ namespace sh stream.unget(); - tok = TOKEN_NewLine; + mToken = TOKEN_NewLine; return; } //Open brace token else if (ch == '{') { - tok = TOKEN_OpenBrace; + mToken = TOKEN_OpenBrace; return; } //Close brace token else if (ch == '}') { - tok = TOKEN_CloseBrace; + mToken = TOKEN_CloseBrace; return; } @@ -139,8 +139,8 @@ namespace sh throw std::runtime_error("Parse Error: Invalid character, ConfigLoader::load()"); } - tokVal = ""; - tok = TOKEN_Text; + mTokenValue = ""; + mToken = TOKEN_Text; do { //Skip comments @@ -157,13 +157,13 @@ namespace sh ch = stream.get(); } while (ch != '\r' && ch != '\n' && !stream.eof()); - tok = TOKEN_NewLine; + mToken = TOKEN_NewLine; return; } } //Add valid char to tokVal - tokVal += (char)ch; + mTokenValue += (char)ch; //Next char ch = stream.get(); @@ -177,7 +177,7 @@ namespace sh void ScriptLoader::_skipNewLines(std::ifstream &stream) { - while (tok == TOKEN_NewLine) + while (mToken == TOKEN_NewLine) { _nextToken(stream); } @@ -189,7 +189,7 @@ namespace sh while (true) { - switch (tok) + switch (mToken) { //Node case TOKEN_Text: @@ -198,23 +198,23 @@ namespace sh ScriptNode *newNode; if (parent) { - newNode = parent->addChild(tokVal); + newNode = parent->addChild(mTokenValue); } else { - newNode = new ScriptNode(0, tokVal); + newNode = new ScriptNode(0, mTokenValue); } //Get values _nextToken(stream); std::string valueStr; int i=0; - while (tok == TOKEN_Text) + while (mToken == TOKEN_Text) { if (i == 0) - valueStr += tokVal; + valueStr += mTokenValue; else - valueStr += " " + tokVal; + valueStr += " " + mTokenValue; _nextToken(stream); ++i; } @@ -235,13 +235,13 @@ namespace sh _skipNewLines(stream); //Add any sub-nodes - if (tok == TOKEN_OpenBrace) + if (mToken == TOKEN_OpenBrace) { //Parse nodes _nextToken(stream); _parseNodes(stream, newNode); //Check for matching closing brace - if (tok != TOKEN_CloseBrace) + if (mToken != TOKEN_CloseBrace) { throw std::runtime_error("Parse Error: Expecting closing brace"); } @@ -249,7 +249,7 @@ namespace sh _skipNewLines(stream); } - newNode->m_fileName = m_currentFileName; + newNode->mFileName = mCurrentFileName; break; } @@ -276,16 +276,16 @@ namespace sh ScriptNode::ScriptNode(ScriptNode *parent, const std::string &name) { - m_name = name; - m_parent = parent; - _removeSelf = true; //For proper destruction - m_lastChildFound = -1; + mName = name; + mParent = parent; + mRemoveSelf = true; //For proper destruction + mLastChildFound = -1; //Add self to parent's child list (unless this is the root node being created) if (parent != NULL) { - m_parent->m_children.push_back(this); - _iter = --(m_parent->m_children.end()); + mParent->mChildren.push_back(this); + mIter = --(mParent->mChildren.end()); } } @@ -293,18 +293,18 @@ namespace sh { //Delete all children std::vector::iterator i; - for (i = m_children.begin(); i != m_children.end(); i++) + for (i = mChildren.begin(); i != mChildren.end(); i++) { ScriptNode *node = *i; - node->_removeSelf = false; + node->mRemoveSelf = false; delete node; } - m_children.clear(); + mChildren.clear(); //Remove self from parent's child list - if (_removeSelf && m_parent != NULL) + if (mRemoveSelf && mParent != NULL) { - m_parent->m_children.erase(_iter); + mParent->mChildren.erase(mIter); } } @@ -324,20 +324,20 @@ namespace sh ScriptNode *ScriptNode::findChild(const std::string &name, bool recursive) { int indx, prevC, nextC; - int childCount = (int)m_children.size(); + int childCount = (int)mChildren.size(); - if (m_lastChildFound != -1) + if (mLastChildFound != -1) { //If possible, try checking the nodes neighboring the last successful search //(often nodes searched for in sequence, so this will boost search speeds). - prevC = m_lastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1; - nextC = m_lastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1; + prevC = mLastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1; + nextC = mLastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1; for (indx = prevC; indx <= nextC; ++indx) { - ScriptNode *node = m_children[indx]; - if (node->m_name == name) + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { - m_lastChildFound = indx; + mLastChildFound = indx; return node; } } @@ -346,17 +346,17 @@ namespace sh //already searched area above. for (indx = nextC + 1; indx < childCount; ++indx) { - ScriptNode *node = m_children[indx]; - if (node->m_name == name) { - m_lastChildFound = indx; + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { + mLastChildFound = indx; return node; } } for (indx = 0; indx < prevC; ++indx) { - ScriptNode *node = m_children[indx]; - if (node->m_name == name) { - m_lastChildFound = indx; + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { + mLastChildFound = indx; return node; } } @@ -365,9 +365,9 @@ namespace sh { //Search for the node from start to finish for (indx = 0; indx < childCount; ++indx){ - ScriptNode *node = m_children[indx]; - if (node->m_name == name) { - m_lastChildFound = indx; + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { + mLastChildFound = indx; return node; } } @@ -378,7 +378,7 @@ namespace sh { for (indx = 0; indx < childCount; ++indx) { - m_children[indx]->findChild(name, recursive); + mChildren[indx]->findChild(name, recursive); } } @@ -389,13 +389,13 @@ namespace sh void ScriptNode::setParent(ScriptNode *newParent) { //Remove self from current parent - m_parent->m_children.erase(_iter); + mParent->mChildren.erase(mIter); //Set new parent - m_parent = newParent; + mParent = newParent; //Add self to new parent - m_parent->m_children.push_back(this); - _iter = --(m_parent->m_children.end()); + mParent->mChildren.push_back(this); + mIter = --(mParent->mChildren.end()); } } diff --git a/extern/shiny/Main/ScriptLoader.hpp b/extern/shiny/Main/ScriptLoader.hpp index caf743bd22..89720fb5d0 100644 --- a/extern/shiny/Main/ScriptLoader.hpp +++ b/extern/shiny/Main/ScriptLoader.hpp @@ -23,7 +23,7 @@ namespace sh ScriptLoader(const std::string& fileEnding); virtual ~ScriptLoader(); - std::string m_fileEnding; + std::string mFileEnding; // For a line like // entity animals/dog @@ -38,11 +38,11 @@ namespace sh void parseScript(std::ifstream &stream); - std::string m_currentFileName; + std::string mCurrentFileName; protected: - float m_LoadOrder; + float mLoadOrder; // like "*.object" std::map m_scriptList; @@ -56,8 +56,8 @@ namespace sh TOKEN_EOF }; - Token tok, lastTok; - std::string tokVal; + Token mToken, mLastToken; + std::string mTokenValue; void _parseNodes(std::ifstream &stream, ScriptNode *parent); void _nextToken(std::ifstream &stream); @@ -74,22 +74,22 @@ namespace sh inline void setName(const std::string &name) { - this->m_name = name; + this->mName = name; } inline std::string &getName() { - return m_name; + return mName; } inline void setValue(const std::string &value) { - m_value = value; + mValue = value; } inline std::string &getValue() { - return m_value; + return mValue; } ScriptNode *addChild(const std::string &name = "untitled", bool replaceExisting = false); @@ -97,36 +97,36 @@ namespace sh inline std::vector &getChildren() { - return m_children; + return mChildren; } inline ScriptNode *getChild(unsigned int index = 0) { - assert(index < m_children.size()); - return m_children[index]; + assert(index < mChildren.size()); + return mChildren[index]; } void setParent(ScriptNode *newParent); inline ScriptNode *getParent() { - return m_parent; + return mParent; } - std::string m_fileName; + std::string mFileName; private: - std::string m_name; - std::string m_value; - std::vector m_children; - ScriptNode *m_parent; + std::string mName; + std::string mValue; + std::vector mChildren; + ScriptNode *mParent; - int m_lastChildFound; //The last child node's index found with a call to findChild() + int mLastChildFound; //The last child node's index found with a call to findChild() - std::vector::iterator _iter; - bool _removeSelf; + std::vector::iterator mIter; + bool mRemoveSelf; }; } From 39d27b87c92bc96b335692e92066eeddfd63b3c9 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 8 Jan 2013 09:14:56 -0800 Subject: [PATCH 07/16] fixed build error with Audiere coded enabled --- apps/openmw/mwsound/audiere_decoder.cpp | 5 +++-- apps/openmw/mwsound/audiere_decoder.hpp | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/audiere_decoder.cpp b/apps/openmw/mwsound/audiere_decoder.cpp index 788d5ae40e..3f3e3a62d2 100644 --- a/apps/openmw/mwsound/audiere_decoder.cpp +++ b/apps/openmw/mwsound/audiere_decoder.cpp @@ -64,7 +64,8 @@ void Audiere_Decoder::open(const std::string &fname) close(); mSoundFile = audiere::FilePtr(new OgreFile(mResourceMgr.openResource(fname))); - mSoundSource = audiere::OpenSampleSource(file); + mSoundSource = audiere::OpenSampleSource(mSoundFile); + mSoundFileName = fname; int channels, srate; audiere::SampleFormat format; @@ -95,7 +96,7 @@ void Audiere_Decoder::close() std::string Audiere_Decoder::getName() { - return mSoundFile->getName(); + return mSoundFileName; } void Audiere_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 91c07ccacb..f432c32ec2 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -12,6 +12,7 @@ namespace MWSound { class Audiere_Decoder : public Sound_Decoder { + std::string mSoundFileName; audiere::FilePtr mSoundFile; audiere::SampleSourcePtr mSoundSource; int mSampleRate; From 135f0870f77304ffdeeea9c4e9a1ecb7e5967267 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 02:16:17 +0000 Subject: [PATCH 08/16] in actionequip, was ignoring actor param, and always using player --- apps/openmw/mwworld/actionequip.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index d8c0196441..3d922a8160 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -15,8 +15,7 @@ namespace MWWorld void ActionEquip::executeImp (const Ptr& actor) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); // slots that this item can be equipped in std::pair, bool> slots = MWWorld::Class::get(getTarget()).getEquipmentSlots(getTarget()); From 62a2ba1cc6cd9dbca65abf5fd174213c8cce6d32 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 03:03:14 +0000 Subject: [PATCH 09/16] beast races cannot equip shoes/boots --- apps/openmw/mwworld/actionequip.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 3d922a8160..f9e7658c8f 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -2,6 +2,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "inventorystore.hpp" #include "player.hpp" @@ -31,11 +32,36 @@ namespace MWWorld } assert(it != invStore.end()); + + std::string npcRace = actor.get()->mBase->mRace; // equip the item in the first free slot for (std::vector::const_iterator slot=slots.first.begin(); slot!=slots.first.end(); ++slot) { + + // Beast races cannot equip shoes / boots + if(npcRace == "argonian" || npcRace == "khajiit") + { + if (*slot == MWWorld::InventoryStore::Slot_Boots) + { + // Only notify the player, not npcs + if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}", std::vector()); + } + + else // It's boots + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); + } + } + } + + break; + } + // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) { From 719663d86eeedd77cce0f576126dd139356e97e7 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 8 Jan 2013 19:52:18 -0800 Subject: [PATCH 10/16] added option to override the activation distance The command line option '--activate-dist ' can be used to override the in game activation distance. --- apps/openmw/engine.cpp | 9 ++++++++- apps/openmw/engine.hpp | 4 ++++ apps/openmw/main.cpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 13 +++++++++++-- apps/openmw/mwworld/worldimp.hpp | 3 ++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index fcfaed47dd..8c288a2ee2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -336,7 +336,8 @@ void OMW::Engine::go() // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap, + mActivationDistanceOverride)); //Load translation data mTranslationDataStorage.setEncoder(&encoder); @@ -509,3 +510,9 @@ void OMW::Engine::setStartupScript (const std::string& path) { mStartupScript = path; } + + +void OMW::Engine::setActivationDistanceOverride (int distance) +{ + mActivationDistanceOverride = distance; +} diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 00889197e8..601cc7765c 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -76,6 +76,7 @@ namespace OMW std::map mFallbackMap; bool mScriptConsoleMode; std::string mStartupScript; + int mActivationDistanceOverride; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; @@ -167,6 +168,9 @@ namespace OMW /// Set path for a script that is run on startup in the console. void setStartupScript (const std::string& path); + /// Override the game setting specified activation distance. + void setActivationDistanceOverride (int distance); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 96dbf89877..d1a498610f 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -149,6 +149,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("fallback", bpo::value()->default_value(FallbackMap(), "") ->multitoken()->composing(), "fallback values") + ("activate-dist", bpo::value ()->default_value (-1), "activation distance override"); + ; bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) @@ -236,6 +238,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setFallbackValues(variables["fallback"].as().mMap); engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); + engine.setActivationDistanceOverride (variables["activate-dist"].as()); return true; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 739d0daab3..766fdafa86 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,10 +170,10 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - ToUTF8::Utf8Encoder* encoder, std::map fallbackMap) + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), - mNumFacing(0) + mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -575,16 +575,25 @@ namespace MWWorld float World::getMaxActivationDistance () { + if (mActivationDistanceOverride >= 0) + return mActivationDistanceOverride; + return (std::max) (getNpcActivationDistance (), getObjectActivationDistance ()); } float World::getNpcActivationDistance () { + if (mActivationDistanceOverride >= 0) + return mActivationDistanceOverride; + return getStore().get().find ("iMaxActivateDist")->getInt()*5/4; } float World::getObjectActivationDistance () { + if (mActivationDistanceOverride >= 0) + return mActivationDistanceOverride; + return getStore().get().find ("iMaxActivateDist")->getInt(); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a1ba6fcdeb..6f8bacf72a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -71,6 +71,7 @@ namespace MWWorld Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); + int mActivationDistanceOverride; std::string mFacedHandle; float mFacedDistance; Ptr mFaced1; @@ -109,7 +110,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - ToUTF8::Utf8Encoder* encoder, std::map fallbackMap); + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap, int mActivationDistanceOverride); virtual ~World(); From 857bb42297f92d2fa3f911cfb9e7e2c0d1660558 Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Wed, 9 Jan 2013 01:44:15 -0400 Subject: [PATCH 11/16] Create a separate scenemanager for each CharacterPreview instance --- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/race.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 13 ++++++++++--- apps/openmw/mwrender/characterpreview.hpp | 4 ++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ffb6c5282e..5390f1602e 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -67,7 +67,7 @@ namespace MWGui setCoord(0, 342, 498, 258); - MWBase::Environment::get().getWorld ()->setupExternalRendering (mPreview); + mPreview.setup(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); openContainer(player); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index d2714fb510..df6c993401 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -106,7 +106,7 @@ void RaceDialog::open() updateSpellPowers(); mPreview = new MWRender::RaceSelectionPreview(); - MWBase::Environment::get().getWorld ()->setupExternalRendering (*mPreview); + mPreview->setup(); mPreview->update (0); const ESM::NPC proto = mPreview->getPrototype(); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 0a11dc2814..b034e098bc 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -35,13 +36,18 @@ namespace MWRender } - void CharacterPreview::setup (Ogre::SceneManager *sceneManager) + void CharacterPreview::setup () { - mSceneMgr = sceneManager; + mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); mCamera = mSceneMgr->createCamera (mName); mCamera->setAspectRatio (float(mSizeX) / float(mSizeY)); - mNode = static_cast(mSceneMgr->getRootSceneNode()->getChild("mwRoot"))->createChildSceneNode (); + Ogre::SceneNode* renderRoot = mSceneMgr->getRootSceneNode()->createChildSceneNode("renderRoot"); + + //we do this with mwRoot in renderingManager, do it here too. + renderRoot->pitch(Ogre::Degree(-90)); + + mNode = renderRoot->createChildSceneNode(); mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), RV_PlayerPreview); @@ -79,6 +85,7 @@ namespace MWRender //Ogre::TextureManager::getSingleton().remove(mName); mSceneMgr->destroyCamera (mName); delete mAnimation; + Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); } void CharacterPreview::rebuild() diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index 18362d1db6..d07a03be7d 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -23,14 +23,14 @@ namespace MWRender class NpcAnimation; - class CharacterPreview : public ExternalRendering + class CharacterPreview { public: CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, Ogre::Vector3 position, Ogre::Vector3 lookAt); virtual ~CharacterPreview(); - virtual void setup (Ogre::SceneManager *sceneManager); + virtual void setup (); virtual void onSetup(); virtual void rebuild(); From 43cd88a24eece09e6f83e586fd3248f4a211636a Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 00:02:57 -0800 Subject: [PATCH 12/16] include members of BSAArchives in Ogres resource indices --- components/bsa/bsa_archive.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 8380b08388..671744a778 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -249,6 +249,13 @@ public: //std::cout << "find(" << pattern << ", " << recursive // << ", " << dirs << ")\n"; StringVectorPtr ptr = StringVectorPtr(new StringVector()); + + BSAFile::FileList const & files = arc.getList (); + + if (pattern == "*") + for (BSAFile::FileList::const_iterator i = files.begin (); i != files.end (); ++i) + ptr->push_back (i->name); + return ptr; } From 67491f6c49b37f02ce376053195c73c9929ec76b Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 09:32:42 -0800 Subject: [PATCH 13/16] reworked DirArchive to improve performance Replaced old file index with a simple map. The map works by storing the file's relative path with path seperators normalized, and in lower case if not in strict mode. Incoming searches are normalized in the same way then the name is searched in the map. The value in the map is the original full path to the file which is then used to created a ConstrainedDataStream. In addition to changing the index, the commonly used Archive methods are implemented so that they don't fall back on the default FileSystemArchive implementations. --- components/bsa/bsa_archive.cpp | 212 +++++++++++++++++---------------- 1 file changed, 112 insertions(+), 100 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 671744a778..b067c5290b 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -29,6 +29,8 @@ #include #include "bsa_file.hpp" +#include "../files/constrainedfiledatastream.hpp" + namespace { @@ -60,104 +62,64 @@ public: static bool fsstrict = false; /// An OGRE Archive wrapping a BSAFile archive -class DirArchive: public Ogre::FileSystemArchive +class DirArchive: public Ogre::Archive { - boost::filesystem::path currentdir; - std::map, ciLessBoost> m; - unsigned int cutoff; + typedef std::map index; - bool findFile(const String& filename, std::string& copy) const + index mIndex; + + static char strict_normalize_char(char ch) { - copy = filename; - std::replace(copy.begin(), copy.end(), '\\', '/'); - - if(copy.at(0) == '/') - copy.erase(0, 1); - - if(fsstrict == true) - return true; - - std::string folder; - //int delimiter = 0; - size_t lastSlash = copy.rfind('/'); - if (lastSlash != std::string::npos) - { - folder = copy.substr(0, lastSlash); - //delimiter = lastSlash+1; - } - - std::vector current; - { - std::map,ciLessBoost>::const_iterator found = m.find(folder); - - if (found == m.end()) - { - return false; - } - else - current = found->second; - } - - std::vector::iterator find = std::lower_bound(current.begin(), current.end(), copy, ciLessBoost()); - if (find != current.end() && !ciLessBoost()(copy, current.front())) - { - if (!boost::iequals(copy, *find)) - if ((find = std::find_if(current.begin(), current.end(), pathComparer(copy))) == current.end()) //\todo Check if this line is actually needed - return false; - - copy = *find; - return true; - } - - return false; + return ch == '\\' ? '/' : ch; } - public: + static char nonstrict_normalize_char(char ch) + { + return ch == '\\' ? '/' : std::tolower (ch); + } + + static std::string normalize_path (std::string::const_iterator begin, std::string::const_iterator end) + { + std::string normalized; + normalized.reserve (end-begin); + char (*normalize_char) (char) = fsstrict ? &strict_normalize_char : &nonstrict_normalize_char; + std::transform (begin, end, std::back_inserter (normalized), normalize_char); + return normalized; + } + + index::const_iterator lookup_filename (std::string const & filename) const + { + std::string normalized = normalize_path (filename.begin (), filename.end ()); + + return mIndex.find (normalized); + } + +public: DirArchive(const String& name) - : FileSystemArchive(name, "Dir"), currentdir (name) + : Archive(name, "Dir") { - mType = "Dir"; - std::string s = name; - cutoff = s.size() + 1; - if(fsstrict == false) - populateMap(currentdir); + typedef boost::filesystem::recursive_directory_iterator directory_iterator; - } - void populateMap(boost::filesystem::path d){ - //need to cut off first - boost::filesystem::directory_iterator dir_iter(d), dir_end; - std::vector filesind; - for(;dir_iter != dir_end; dir_iter++) - { - if(boost::filesystem::is_directory(*dir_iter)) - populateMap(*dir_iter); - else + directory_iterator end; + + size_t prefix = name.size (); + + if (name.size () > 0 && name [prefix - 1] != '\\' && name [prefix - 1] != '/') + ++prefix; + + for (directory_iterator i (name); i != end; ++i) { - std::string s = dir_iter->path().string(); - std::replace(s.begin(), s.end(), '\\', '/'); + if(boost::filesystem::is_directory (*i)) + continue; - std::string small; - if(cutoff < s.size()) - small = s.substr(cutoff, s.size() - cutoff); - else - small = s.substr(cutoff - 1, s.size() - cutoff); + std::string proper = i->path ().string (); - filesind.push_back(small); + std::string searchable = normalize_path (proper.begin () + prefix, proper.end ()); + + mIndex.insert (std::make_pair (std::move (searchable), std::move (proper))); } } - std::sort(filesind.begin(), filesind.end(), ciLessBoost()); - - std::string small; - std::string original = d.string(); - std::replace(original.begin(), original.end(), '\\', '/'); - if(cutoff < original.size()) - small = original.substr(cutoff, original.size() - cutoff); - else - small = original.substr(cutoff - 1, original.size() - cutoff); - - m[small] = filesind; - } bool isCaseSensitive() const { return fsstrict; } @@ -165,26 +127,76 @@ class DirArchive: public Ogre::FileSystemArchive void load() {} void unload() {} - bool exists(const String& filename) { - std::string copy; - - if (findFile(filename, copy)) - return FileSystemArchive::exists(copy); - - return false; - } - DataStreamPtr open(const String& filename, bool readonly = true) const - { - std::string copy; + { + index::const_iterator i = lookup_filename (filename); - if (findFile(filename, copy)) - return FileSystemArchive::open(copy, readonly); + if (i == mIndex.end ()) + { + std::ostringstream os; + os << "The file '" << filename << "' could not be found."; + throw std::runtime_error (os.str ()); + } - DataStreamPtr p; - return p; - } + return openConstrainedFileDataStream (i->second.c_str ()); + } + StringVectorPtr list(bool recursive = true, bool dirs = false) + { + StringVectorPtr ptr = StringVectorPtr(new StringVector()); + std::cout << "DirArchive<" << getName () << ">::list" << std::endl; + return ptr; + } + + FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false) + { + FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + std::cout << "DirArchive<" << getName () << ">::listFileInfo" << std::endl; + return ptr; + } + + StringVectorPtr find(const String& pattern, bool recursive = true, + bool dirs = false) + { + StringVectorPtr ptr = StringVectorPtr(new StringVector()); + + if (pattern == "*") + for (index::const_iterator i = mIndex.begin (); i != mIndex.end (); ++i) + ptr->push_back (i->first); + + return ptr; + } + + bool exists(const String& filename) + { + return lookup_filename (filename) != mIndex.end (); + } + + time_t getModifiedTime(const String&) { return 0; } + + FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, + bool dirs = false) const + { + FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + + index::const_iterator i = lookup_filename (pattern); + + if (i != mIndex.end ()) + { + FileInfo fi; + + std::size_t npos = i->first.rfind ('/'); + + fi.archive = this; + fi.filename = npos != -1 ? i->first.substr (npos) : i->first; + fi.path = npos != -1 ? i->first.substr (0, npos) : ""; + fi.compressedSize = fi.uncompressedSize = 0; + + ptr->push_back(fi); + } + + return ptr; + } }; class BSAArchive : public Archive From d1e51ebf426b47e6c1d4e35775ca66407ef474fd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 9 Jan 2013 12:25:45 +0100 Subject: [PATCH 14/16] silenced some warnings --- components/bsa/bsa_archive.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index b067c5290b..011b81351d 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -188,8 +188,8 @@ public: std::size_t npos = i->first.rfind ('/'); fi.archive = this; - fi.filename = npos != -1 ? i->first.substr (npos) : i->first; - fi.path = npos != -1 ? i->first.substr (0, npos) : ""; + fi.filename = npos != std::string::npos ? i->first.substr (npos) : i->first; + fi.path = npos != std::string::npos ? i->first.substr (0, npos) : ""; fi.compressedSize = fi.uncompressedSize = 0; ptr->push_back(fi); From b035a5aa4a3cb4be7df7a5ac7befc25a10f1202b Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 13:18:05 +0000 Subject: [PATCH 15/16] beast races cannot equip anything they oughtn't to be able to equip --- apps/openmw/mwworld/actionequip.cpp | 49 +++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index f9e7658c8f..0b4ca95ed1 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -43,23 +43,46 @@ namespace MWWorld // Beast races cannot equip shoes / boots if(npcRace == "argonian" || npcRace == "khajiit") { - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - // Only notify the player, not npcs - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - { - if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}", std::vector()); - } + if(*slot == MWWorld::InventoryStore::Slot_Helmet){ + std::vector parts; - else // It's boots - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); - } + if(it.getType() == MWWorld::ContainerStore::Type_Clothing) + parts = it->get()->mBase->mParts.mParts; + else + parts = it->get()->mBase->mParts.mParts; + + bool allow = true; + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PartReferenceType::PRT_Head) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}", std::vector()); + allow = false; + break; } + } + + if(!allow) + break; + } + + if (*slot == MWWorld::InventoryStore::Slot_Boots) + { + // Only notify the player, not npcs + if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}", std::vector()); + } + + else // It's boots + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); + } + } + break; } - break; } // if all slots are occupied, replace the last slot From 375961fe5ea4c516878f08e11fbe492d3aadf6b6 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 13:27:12 +0000 Subject: [PATCH 16/16] small fix --- apps/openmw/mwworld/actionequip.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 0b4ca95ed1..60260a812b 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -40,7 +40,7 @@ namespace MWWorld slot!=slots.first.end(); ++slot) { - // Beast races cannot equip shoes / boots + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) if(npcRace == "argonian" || npcRace == "khajiit") { if(*slot == MWWorld::InventoryStore::Slot_Helmet){ @@ -56,7 +56,9 @@ namespace MWWorld { if((*itr).mPart == ESM::PartReferenceType::PRT_Head) { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}", std::vector()); + if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}", std::vector()); + allow = false; break; }