mirror of
https://github.com/OpenMW/openmw.git
synced 2025-05-01 20:41:24 +00:00
Merge remote-tracking branch 'jordan-ayers/bugfix/1533'
This commit is contained in:
commit
8e007c91fd
3 changed files with 146 additions and 54 deletions
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <typeinfo>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
#include "livecellref.hpp"
|
#include "livecellref.hpp"
|
||||||
|
@ -181,7 +183,12 @@ namespace MWWorld
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
CellRefList<T>& get() {
|
CellRefList<T>& 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 <class T>
|
||||||
|
const CellRefList<T>& getReadOnly() {
|
||||||
|
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;
|
bool isPointConnected(const int start, const int end) const;
|
||||||
|
@ -357,6 +364,12 @@ namespace MWWorld
|
||||||
return mWeapons;
|
return mWeapons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline const CellRefList<ESM::Door>& CellStore::getReadOnly<ESM::Door>()
|
||||||
|
{
|
||||||
|
return mDoors;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator== (const CellStore& left, const CellStore& right);
|
bool operator== (const CellStore& left, const CellStore& right);
|
||||||
bool operator!= (const CellStore& left, const CellStore& right);
|
bool operator!= (const CellStore& left, const CellStore& right);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2784,18 +2784,45 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
if (cell->isExterior())
|
if (cell->isExterior())
|
||||||
return false;
|
return false;
|
||||||
MWWorld::CellRefList<ESM::Door>& doors = cell->get<ESM::Door>();
|
|
||||||
CellRefList<ESM::Door>::List& refList = doors.mList;
|
|
||||||
|
|
||||||
// Check if any door in the cell leads to an exterior directly
|
// Search for a 'nearest' exterior, counting each cell between the starting
|
||||||
for (CellRefList<ESM::Door>::List::iterator it = refList.begin(); it != refList.end(); ++it)
|
// cell and the exterior as a distance of 1. Will fail for isolated interiors.
|
||||||
{
|
std::set< std::string >checkedCells;
|
||||||
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
std::set< std::string >currentCells;
|
||||||
if (ref.mRef.getTeleport() && ref.mRef.getDestCell().empty())
|
std::set< std::string >nextCells;
|
||||||
{
|
nextCells.insert( cell->getCell()->mName );
|
||||||
ESM::Position pos = ref.mRef.getDoorDest();
|
|
||||||
result = Ogre::Vector3(pos.pos);
|
while ( !nextCells.empty() ) {
|
||||||
return true;
|
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;
|
||||||
|
|
||||||
|
const MWWorld::CellRefList<ESM::Door>& doors = next->getReadOnly<ESM::Door>();
|
||||||
|
const CellRefList<ESM::Door>::List& refList = doors.mList;
|
||||||
|
|
||||||
|
// Check if any door in the cell leads to an exterior directly
|
||||||
|
for (CellRefList<ESM::Door>::List::const_iterator it = refList.begin(); it != refList.end(); ++it)
|
||||||
|
{
|
||||||
|
const MWWorld::LiveCellRef<ESM::Door>& 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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2803,33 +2830,93 @@ namespace MWWorld
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id )
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
MWWorld::Ptr closestMarker;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
closestMarker = next->search( id );
|
||||||
|
if ( !closestMarker.isEmpty() )
|
||||||
|
{
|
||||||
|
return closestMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MWWorld::CellRefList<ESM::Door>& doors = next->getReadOnly<ESM::Door>();
|
||||||
|
const CellRefList<ESM::Door>::List& doorList = doors.mList;
|
||||||
|
|
||||||
|
// Check if any door in the cell leads to an exterior directly
|
||||||
|
for (CellRefList<ESM::Door>::List::const_iterator it = doorList.begin(); it != doorList.end(); ++it)
|
||||||
|
{
|
||||||
|
const MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
||||||
|
|
||||||
|
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<MWWorld::Ptr> markers;
|
||||||
|
mCells.getExteriorPtrs(id, markers);
|
||||||
|
for (std::vector<MWWorld::Ptr>::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,
|
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
|
||||||
const std::string& id)
|
const std::string& id)
|
||||||
{
|
{
|
||||||
Ogre::Vector3 worldPos;
|
MWWorld::Ptr closestMarker = getClosestMarker( ptr, id );
|
||||||
if (!findInteriorPositionInWorldSpace(ptr.getCell(), worldPos))
|
|
||||||
worldPos = mPlayer->getLastKnownExteriorPosition();
|
|
||||||
|
|
||||||
MWWorld::Ptr closestMarker;
|
if ( closestMarker.isEmpty() )
|
||||||
float closestDistance = FLT_MAX;
|
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> markers;
|
|
||||||
mCells.getExteriorPtrs(id, markers);
|
|
||||||
|
|
||||||
for (std::vector<MWWorld::Ptr>::iterator it = markers.begin(); it != markers.end(); ++it)
|
|
||||||
{
|
{
|
||||||
ESM::Position pos = it->getRefData().getPosition();
|
std::cerr << "Failed to teleport: no closest marker found" << std::endl;
|
||||||
Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos);
|
return;
|
||||||
float distance = worldPos.squaredDistance(markerPos);
|
|
||||||
if (distance < closestDistance)
|
|
||||||
{
|
|
||||||
closestDistance = distance;
|
|
||||||
closestMarker = *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
action.execute(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2978,31 +3065,21 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::confiscateStolenItems(const Ptr &ptr)
|
void World::confiscateStolenItems(const Ptr &ptr)
|
||||||
{
|
{
|
||||||
Ogre::Vector3 playerPos;
|
MWWorld::Ptr prisonMarker = getClosestMarker( ptr, "prisonmarker" );
|
||||||
if (!findInteriorPositionInWorldSpace(ptr.getCell(), playerPos))
|
std::string prisonName = prisonMarker.mRef->mRef.getDestCell();
|
||||||
playerPos = mPlayer->getLastKnownExteriorPosition();
|
if ( prisonName.empty() )
|
||||||
|
|
||||||
MWWorld::Ptr closestChest;
|
|
||||||
float closestDistance = FLT_MAX;
|
|
||||||
|
|
||||||
//Find closest stolen_goods chest
|
|
||||||
std::vector<MWWorld::Ptr> chests;
|
|
||||||
mCells.getInteriorPtrs("stolen_goods", chests);
|
|
||||||
|
|
||||||
Ogre::Vector3 chestPos;
|
|
||||||
for (std::vector<MWWorld::Ptr>::iterator it = chests.begin(); it != chests.end(); ++it)
|
|
||||||
{
|
{
|
||||||
if (!findInteriorPositionInWorldSpace(it->getCell(), chestPos))
|
std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl;
|
||||||
continue;
|
return;
|
||||||
|
}
|
||||||
float distance = playerPos.squaredDistance(chestPos);
|
MWWorld::CellStore *prison = getInterior( prisonName );
|
||||||
if (distance < closestDistance)
|
if ( !prison )
|
||||||
{
|
{
|
||||||
closestDistance = distance;
|
std::cerr << "Failed to confiscate items: failed to load cell " << prisonName << std::endl;
|
||||||
closestChest = *it;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr closestChest = prison->search( "stolen_goods" );
|
||||||
if (!closestChest.isEmpty()) //Found a close chest
|
if (!closestChest.isEmpty()) //Found a close chest
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);
|
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);
|
||||||
|
|
|
@ -150,6 +150,8 @@ namespace MWWorld
|
||||||
|
|
||||||
float feetToGameUnits(float feet);
|
float feetToGameUnits(float feet);
|
||||||
|
|
||||||
|
MWWorld::Ptr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
World (OEngine::Render::OgreRenderer& renderer,
|
World (OEngine::Render::OgreRenderer& renderer,
|
||||||
|
|
Loading…
Reference in a new issue