From 1d18d3ff4cb5a6f28b7f0d7ca03f076a5794a447 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Thu, 12 Feb 2015 22:27:47 -0600 Subject: [PATCH 1/6] Add a full search to findInteriorPositionInWorldSpace. Part of OMW Bug #1533 Implement a search for one of the 'nearest' exterior cells. In this case, 'nearest' means the fewest number of cells away via door markers. This causes the world map position to update immediately after teleporting, unless the new cell has no connecting path to an exterior. Intervention spells and Jail travel will be much closer to vanialla Morrowind, except for in Mournhold. --- apps/openmw/mwworld/worldimp.cpp | 49 +++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 188f3cdad..23cef1596 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2784,18 +2784,45 @@ namespace MWWorld { if (cell->isExterior()) return false; - MWWorld::CellRefList& doors = cell->get(); - CellRefList::List& refList = doors.mList; - // Check if any door in the cell leads to an exterior directly - for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) - { - MWWorld::LiveCellRef& ref = *it; - if (ref.mRef.getTeleport() && ref.mRef.getDestCell().empty()) - { - ESM::Position pos = ref.mRef.getDoorDest(); - result = Ogre::Vector3(pos.pos); - return true; + // Search for a 'nearest' exterior, counting each cell between the starting + // cell and the exterior as a distance of 1. Will fail for isolated interiors. + std::set< std::string >checkedCells; + std::set< std::string >currentCells; + std::set< std::string >nextCells; + nextCells.insert( cell->getCell()->mName ); + + while ( !nextCells.empty() ) { + currentCells = nextCells; + nextCells.clear(); + for( std::set< std::string >::const_iterator i = currentCells.begin(); i != currentCells.end(); ++i ) { + MWWorld::CellStore *next = getInterior( *i ); + if ( !next ) continue; + + MWWorld::CellRefList& doors = next->get(); + CellRefList::List& refList = doors.mList; + + // Check if any door in the cell leads to an exterior directly + for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) + { + MWWorld::LiveCellRef& ref = *it; + if (!ref.mRef.getTeleport()) continue; + + if (ref.mRef.getDestCell().empty()) + { + ESM::Position pos = ref.mRef.getDoorDest(); + result = Ogre::Vector3(pos.pos); + return true; + } + else + { + std::string dest = ref.mRef.getDestCell(); + if ( !checkedCells.count(dest) && !currentCells.count(dest) ) + nextCells.insert(dest); + } + } + + checkedCells.insert( *i ); } } From 5ef78903dc28554ad58784ac13f9aba1a135c5ad Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Sat, 14 Feb 2015 15:43:09 -0600 Subject: [PATCH 2/6] Teleportation: Support markers in Mournhold. OMW Bug #1533 Note: the 'stolen goods' search is not yet correct for Mournhald. --- apps/openmw/mwworld/worldimp.cpp | 104 +++++++++++++++++++++++++------ apps/openmw/mwworld/worldimp.hpp | 2 + 2 files changed, 87 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 23cef1596..fda73238a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2830,33 +2830,99 @@ namespace MWWorld return false; } - void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, - const std::string& id) + MWWorld::Ptr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ) { - Ogre::Vector3 worldPos; - if (!findInteriorPositionInWorldSpace(ptr.getCell(), worldPos)) - worldPos = mPlayer->getLastKnownExteriorPosition(); + // Search for a 'nearest' marker, counting each cell between the starting + // cell and the exterior as a distance of 1. If an exterior is found, jump + // to the nearest exterior marker, without further interior searching. + std::set< std::string >checkedCells; + std::set< std::string >currentCells; + std::set< std::string >nextCells; + nextCells.insert( ptr.getCell()->getCell()->mName ); + while ( !nextCells.empty() ) { + currentCells = nextCells; + nextCells.clear(); + for( std::set< std::string >::const_iterator i = currentCells.begin(); i != currentCells.end(); ++i ) { + MWWorld::CellStore *next = getInterior( *i ); + checkedCells.insert( *i ); + if ( !next ) continue; - MWWorld::Ptr closestMarker; - float closestDistance = FLT_MAX; + MWWorld::CellRefList& statics = next->get(); + CellRefList::List& staticList = statics.mList; + for (CellRefList::List::iterator it = staticList.begin(); it != staticList.end(); ++it) + { + MWWorld::LiveCellRef& ref = *it; + if ( id == ref.mRef.getRefId() ) { + return MWWorld::Ptr( &ref, next ); + } + } - std::vector markers; - mCells.getExteriorPtrs(id, markers); + MWWorld::CellRefList& doors = next->get(); + CellRefList::List& doorList = doors.mList; - for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) - { - ESM::Position pos = it->getRefData().getPosition(); - Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos); - float distance = worldPos.squaredDistance(markerPos); - if (distance < closestDistance) - { - closestDistance = distance; - closestMarker = *it; + // Check if any door in the cell leads to an exterior directly + for (CellRefList::List::iterator it = doorList.begin(); it != doorList.end(); ++it) + { + MWWorld::LiveCellRef& ref = *it; + + if ( id == ref.mRef.getRefId() ) { + return MWWorld::Ptr( &ref, next ); + } + + if (!ref.mRef.getTeleport()) continue; + + if (ref.mRef.getDestCell().empty()) + { + Ogre::Vector3 worldPos = Ogre::Vector3(ref.mRef.getDoorDest().pos); + float closestDistance = FLT_MAX; + + MWWorld::Ptr closestMarker; + std::vector markers; + mCells.getExteriorPtrs(id, markers); + for (std::vector::iterator it2 = markers.begin(); it2 != markers.end(); ++it2) + { + ESM::Position pos = it2->getRefData().getPosition(); + Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos); + float distance = worldPos.squaredDistance(markerPos); + if (distance < closestDistance) + { + closestDistance = distance; + closestMarker = *it2; + } + + } + + return closestMarker; + } + else + { + std::string dest = ref.mRef.getDestCell(); + if ( !checkedCells.count(dest) && !currentCells.count(dest) ) + nextCells.insert(dest); + } + } } + } + return MWWorld::Ptr(); + } + + void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id) + { + MWWorld::Ptr closestMarker = getClosestMarker( ptr, id ); + + if ( closestMarker.isEmpty() ) + { + std::cerr << "Failed to teleport: no closest marker found" << std::endl; + return; } - MWWorld::ActionTeleport action("", closestMarker.getRefData().getPosition()); + std::string cellName; + if ( !closestMarker.mCell->isExterior() ) + cellName = closestMarker.mCell->getCell()->mName; + + MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition()); action.execute(ptr); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 9980f6f90..7a3505fd4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -151,6 +151,8 @@ namespace MWWorld float feetToGameUnits(float feet); + MWWorld::Ptr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ); + public: World (OEngine::Render::OgreRenderer& renderer, From 6d1aec6970ef753e08e687c06eb80cdfd403cc9a Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Sat, 14 Feb 2015 16:09:17 -0600 Subject: [PATCH 3/6] Confiscate stolen goods: Support Mournhold prisons. OMW Bug #1533 --- apps/openmw/mwworld/worldimp.cpp | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fda73238a..54385cf83 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3071,28 +3071,29 @@ namespace MWWorld void World::confiscateStolenItems(const Ptr &ptr) { - Ogre::Vector3 playerPos; - if (!findInteriorPositionInWorldSpace(ptr.getCell(), playerPos)) - playerPos = mPlayer->getLastKnownExteriorPosition(); + MWWorld::Ptr prisonMarker = getClosestMarker( ptr, "prisonmarker" ); + std::string prisonName = prisonMarker.mRef->mRef.getDestCell(); + if ( prisonName.empty() ) + { + std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl; + return; + } + MWWorld::CellStore *prison = getInterior( prisonName ); + if ( !prison ) + { + std::cerr << "Failed to confiscate items: failed to load cell " << prisonName << std::endl; + return; + } MWWorld::Ptr closestChest; - float closestDistance = FLT_MAX; - - //Find closest stolen_goods chest - std::vector chests; - mCells.getInteriorPtrs("stolen_goods", chests); - Ogre::Vector3 chestPos; - for (std::vector::iterator it = chests.begin(); it != chests.end(); ++it) + MWWorld::CellRefList& containers = prison->get(); + CellRefList::List& refList = containers.mList; + for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) { - if (!findInteriorPositionInWorldSpace(it->getCell(), chestPos)) - continue; - - float distance = playerPos.squaredDistance(chestPos); - if (distance < closestDistance) - { - closestDistance = distance; - closestChest = *it; + MWWorld::LiveCellRef& ref = *it; + if ( ref.mRef.getRefId() == "stolen_goods" ) { + closestChest = MWWorld::Ptr( &ref, prison ); } } From c21b59ecff764b774a3db95778c6434c04e0f8d5 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Sat, 21 Feb 2015 15:55:22 -0600 Subject: [PATCH 4/6] Teleportation: Avoid marking searched cells as changed. OMW Bug #1533 Only mark cells with the target marker / evidence chest as 'changed'. --- apps/openmw/mwworld/cellstore.hpp | 11 +++++++++++ apps/openmw/mwworld/worldimp.cpp | 33 ++++++++----------------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index f6f2a3b48..1d4e99532 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -184,6 +184,11 @@ namespace MWWorld throw std::runtime_error ("Storage for this type not exist in cells"); } + template + CellRefList& getReadOnly() { + throw std::runtime_error ("Read Only access not available for this type"); + } + bool isPointConnected(const int start, const int end) const; std::list aStarSearch(const int start, const int end) const; @@ -357,6 +362,12 @@ namespace MWWorld return mWeapons; } + template<> + inline CellRefList& CellStore::getReadOnly() + { + return mDoors; + } + bool operator== (const CellStore& left, const CellStore& right); bool operator!= (const CellStore& left, const CellStore& right); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 54385cf83..dd730b32a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2799,7 +2799,7 @@ namespace MWWorld MWWorld::CellStore *next = getInterior( *i ); if ( !next ) continue; - MWWorld::CellRefList& doors = next->get(); + MWWorld::CellRefList& doors = next->getReadOnly(); CellRefList::List& refList = doors.mList; // Check if any door in the cell leads to an exterior directly @@ -2838,6 +2838,8 @@ namespace MWWorld std::set< std::string >checkedCells; std::set< std::string >currentCells; std::set< std::string >nextCells; + MWWorld::Ptr closestMarker; + nextCells.insert( ptr.getCell()->getCell()->mName ); while ( !nextCells.empty() ) { currentCells = nextCells; @@ -2847,17 +2849,13 @@ namespace MWWorld checkedCells.insert( *i ); if ( !next ) continue; - MWWorld::CellRefList& statics = next->get(); - CellRefList::List& staticList = statics.mList; - for (CellRefList::List::iterator it = staticList.begin(); it != staticList.end(); ++it) + closestMarker = next->search( id ); + if ( !closestMarker.isEmpty() ) { - MWWorld::LiveCellRef& ref = *it; - if ( id == ref.mRef.getRefId() ) { - return MWWorld::Ptr( &ref, next ); - } + return closestMarker; } - MWWorld::CellRefList& doors = next->get(); + MWWorld::CellRefList& doors = next->getReadOnly(); CellRefList::List& doorList = doors.mList; // Check if any door in the cell leads to an exterior directly @@ -2865,10 +2863,6 @@ namespace MWWorld { MWWorld::LiveCellRef& ref = *it; - if ( id == ref.mRef.getRefId() ) { - return MWWorld::Ptr( &ref, next ); - } - if (!ref.mRef.getTeleport()) continue; if (ref.mRef.getDestCell().empty()) @@ -3085,18 +3079,7 @@ namespace MWWorld return; } - MWWorld::Ptr closestChest; - - MWWorld::CellRefList& containers = prison->get(); - CellRefList::List& refList = containers.mList; - for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) - { - MWWorld::LiveCellRef& ref = *it; - if ( ref.mRef.getRefId() == "stolen_goods" ) { - closestChest = MWWorld::Ptr( &ref, prison ); - } - } - + MWWorld::Ptr closestChest = prison->search( "stolen_goods" ); if (!closestChest.isEmpty()) //Found a close chest { MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest); From 399259a95cb1632ec1f183602c4dc77564786308 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Sun, 22 Feb 2015 12:12:54 -0600 Subject: [PATCH 5/6] Improve CellStore exception messages. --- apps/openmw/mwworld/cellstore.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 1d4e99532..cd7af5ad6 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include "livecellref.hpp" @@ -181,12 +183,12 @@ namespace MWWorld template CellRefList& get() { - throw std::runtime_error ("Storage for this type not exist in cells"); + throw std::runtime_error ("Storage for type " + std::string(typeid(T).name())+ " does not exist in cells"); } template CellRefList& getReadOnly() { - throw std::runtime_error ("Read Only access not available for this type"); + throw std::runtime_error ("Read Only CellRefList access not available for type " + std::string(typeid(T).name()) ); } bool isPointConnected(const int start, const int end) const; From 5edafc2a4cf50afb0ef083df70ae245779219697 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Sun, 22 Feb 2015 12:25:10 -0600 Subject: [PATCH 6/6] Cleanup: Add const to read-only CellRefList access. OMW Bug #1533 --- apps/openmw/mwworld/cellstore.hpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index cd7af5ad6..d7036d6b1 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -187,7 +187,7 @@ namespace MWWorld } template - CellRefList& getReadOnly() { + const CellRefList& getReadOnly() { throw std::runtime_error ("Read Only CellRefList access not available for type " + std::string(typeid(T).name()) ); } @@ -365,7 +365,7 @@ namespace MWWorld } template<> - inline CellRefList& CellStore::getReadOnly() + inline const CellRefList& CellStore::getReadOnly() { return mDoors; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dd730b32a..5f42a5379 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2799,13 +2799,13 @@ namespace MWWorld MWWorld::CellStore *next = getInterior( *i ); if ( !next ) continue; - MWWorld::CellRefList& doors = next->getReadOnly(); - CellRefList::List& refList = doors.mList; + const MWWorld::CellRefList& doors = next->getReadOnly(); + const CellRefList::List& refList = doors.mList; // Check if any door in the cell leads to an exterior directly - for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) + for (CellRefList::List::const_iterator it = refList.begin(); it != refList.end(); ++it) { - MWWorld::LiveCellRef& ref = *it; + const MWWorld::LiveCellRef& ref = *it; if (!ref.mRef.getTeleport()) continue; if (ref.mRef.getDestCell().empty()) @@ -2855,13 +2855,13 @@ namespace MWWorld return closestMarker; } - MWWorld::CellRefList& doors = next->getReadOnly(); - CellRefList::List& doorList = doors.mList; + const MWWorld::CellRefList& doors = next->getReadOnly(); + const CellRefList::List& doorList = doors.mList; // Check if any door in the cell leads to an exterior directly - for (CellRefList::List::iterator it = doorList.begin(); it != doorList.end(); ++it) + for (CellRefList::List::const_iterator it = doorList.begin(); it != doorList.end(); ++it) { - MWWorld::LiveCellRef& ref = *it; + const MWWorld::LiveCellRef& ref = *it; if (!ref.mRef.getTeleport()) continue;