1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 02:09:43 +00:00

Dest Door and teleport use ESM::RefId

This changes a lot of files as a consequence.
Still buggy, moving to exterior doesn't bring to the right place yet
coc "seyda neen" doesn't work. SO I broke somehting when fetching a cell from a name
This commit is contained in:
florent.teppe 2023-02-19 17:42:37 +01:00
parent 96e42d1666
commit 3f678c3b0a
26 changed files with 258 additions and 170 deletions

View file

@ -114,7 +114,7 @@ namespace MWBase
{
std::string name;
float x, y; // world position
ESM::CellId dest;
ESM::RefId dest;
};
World() {}
@ -260,6 +260,9 @@ namespace MWBase
virtual void changeToCell(
const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true)
= 0;
virtual void changeToCell(
const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true)
= 0;
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
virtual MWWorld::Ptr getFacedObject() = 0;
@ -514,13 +517,16 @@ namespace MWBase
virtual bool screenshot360(osg::Image* image) = 0;
/// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise
virtual bool findExteriorPosition(std::string_view name, ESM::Position& pos) = 0;
/// \return invalid RefId if exterior with given name not exists, the cell's RefId otherwise
virtual ESM::RefId findExteriorPosition(std::string_view name, ESM::Position& pos) = 0;
/// Find default position inside interior cell specified by name
/// \return false if interior with given name not exists, true otherwise
virtual bool findInteriorPosition(std::string_view name, ESM::Position& pos) = 0;
/// \return invalid RefId if interior with given name not exists, the cell's RefId otherwise
virtual ESM::RefId findInteriorPosition(std::string_view name, ESM::Position& pos) = 0;
/// Find default position inside interior or exterior cell specified by name
/// \return invalid RefId if interior with given name not exists, the cell's RefId otherwise
virtual ESM::RefId findCellPosition(std::string_view cellName, ESM::Position& pos) = 0;
/// Enables or disables use of teleport spell effects (recall, intervention, etc).
virtual void enableTeleporting(bool enable) = 0;

View file

@ -23,6 +23,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwworld/failedaction.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/worldmodel.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwgui/ustring.hpp"
@ -298,16 +299,8 @@ namespace MWClass
std::string Door::getDestination(const MWWorld::LiveCellRef<ESM::Door>& door)
{
std::string_view dest = door.mRef.getDestCell();
if (dest.empty())
{
// door leads to exterior, use cell name (if any), otherwise translated region name
auto world = MWBase::Environment::get().getWorld();
const osg::Vec2i index
= MWWorld::positionToCellIndex(door.mRef.getDoorDest().pos[0], door.mRef.getDoorDest().pos[1]);
const ESM::Cell* cell = world->getStore().get<ESM::Cell>().search(index.x(), index.y());
dest = world->getCellName(cell);
}
std::string_view dest
= MWBase::Environment::get().getWorldModel()->getCell(door.mRef.getDestCell())->getCell()->getDisplayName();
return "#{sCell=" + std::string{ dest } + "}";
}

View file

@ -158,7 +158,7 @@ namespace MWGui
return mMarkers.end();
}
CustomMarkerCollection::RangeType CustomMarkerCollection::getMarkers(const ESM::CellId& cellId) const
CustomMarkerCollection::RangeType CustomMarkerCollection::getMarkers(const ESM::RefId& cellId) const
{
return mMarkers.equal_range(cellId);
}
@ -356,8 +356,8 @@ namespace MWGui
cellId.mWorldspace = (mInterior ? mPrefix : ESM::CellId::sDefaultWorldspace);
cellId.mIndex.mX = mCurX + dX;
cellId.mIndex.mY = mCurY + dY;
CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellId);
ESM::RefId cellRefId = cellId.getCellRefId();
CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellRefId);
for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second;
++it)
{
@ -885,16 +885,19 @@ namespace MWGui
mEditingMarker.mWorldX = worldPos.x();
mEditingMarker.mWorldY = worldPos.y();
ESM::CellId clickedId;
mEditingMarker.mCell.mPaged = !mInterior;
clickedId.mPaged = !mInterior;
if (mInterior)
mEditingMarker.mCell.mWorldspace = LocalMapBase::mPrefix;
clickedId.mWorldspace = LocalMapBase::mPrefix;
else
{
mEditingMarker.mCell.mWorldspace = ESM::CellId::sDefaultWorldspace;
mEditingMarker.mCell.mIndex.mX = x;
mEditingMarker.mCell.mIndex.mY = y;
clickedId.mWorldspace = ESM::CellId::sDefaultWorldspace;
clickedId.mIndex.mX = x;
clickedId.mIndex.mY = y;
}
mEditingMarker.mCell = clickedId.getCellRefId();
mEditingMarker.mCellId = clickedId;
mEditNoteDialog.setVisible(true);
mEditNoteDialog.showDeleteButton(false);
@ -1125,7 +1128,8 @@ namespace MWGui
cellId.mIndex.mY = y;
cellId.mWorldspace = ESM::CellId::sDefaultWorldspace;
cellId.mPaged = true;
CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellId);
ESM::RefId cellRefId = cellId.getCellRefId();
CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellRefId);
std::vector<std::string> destNotes;
for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it)
destNotes.push_back(it->second.mNote);

View file

@ -56,14 +56,14 @@ namespace MWGui
size_t size() const;
typedef std::multimap<ESM::CellId, ESM::CustomMarker> ContainerType;
typedef std::multimap<ESM::RefId, ESM::CustomMarker> ContainerType;
typedef std::pair<ContainerType::const_iterator, ContainerType::const_iterator> RangeType;
ContainerType::const_iterator begin() const;
ContainerType::const_iterator end() const;
RangeType getMarkers(const ESM::CellId& cellId) const;
RangeType getMarkers(const ESM::RefId& cellId) const;
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
EventHandle_Void eventMarkersChanged;

View file

@ -195,9 +195,15 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);
ESM::CellId cellId;
osg::Vec2i posCell = MWWorld::positionToCellIndex(pos.pos[0], pos.pos[1]);
cellId.mPaged = !interior;
cellId.mWorldspace = Misc::StringUtils::lowerCase(cellname);
cellId.mIndex.mX = posCell.x();
cellId.mIndex.mY = posCell.y();
// Teleports any followers, too.
MWWorld::ActionTeleport action(interior ? cellname : "", pos, true);
MWWorld::ActionTeleport action(cellId.getCellRefId(), pos, true);
action.execute(player);
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);

View file

@ -36,8 +36,7 @@ namespace MWLua
const MWWorld::CellRef& cellRef = doorPtr(o).getCellRef();
if (!cellRef.getTeleport())
return sol::nil;
MWWorld::CellStore* cell = MWBase::Environment::get().getWorldModel()->getCellByPosition(
cellRef.getDoorDest().asVec3(), cellRef.getDestCell());
MWWorld::CellStore* cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell());
assert(cell);
return o.getCell(lua, cell);
};

View file

@ -487,9 +487,9 @@ namespace MWMechanics
world->getPlayer().getMarkedPosition(markedCell, markedPosition);
if (markedCell)
{
std::string_view dest;
ESM::RefId dest;
if (!markedCell->isExterior())
dest = markedCell->getCell()->getNameId();
dest = markedCell->getCell()->getId();
MWWorld::ActionTeleport action(dest, markedPosition, false);
action.execute(target);
if (!caster.isEmpty())

View file

@ -92,19 +92,9 @@ namespace MWScript
ESM::Position pos;
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Ptr playerPtr = world->getPlayerPtr();
if (world->findExteriorPosition(cell, pos))
{
MWWorld::ActionTeleport({}, pos, false).execute(playerPtr);
world->adjustPosition(playerPtr, false);
}
else
{
// Change to interior even if findInteriorPosition()
// yields false. In this case position will be zero-point.
world->findInteriorPosition(cell, pos);
MWWorld::ActionTeleport(cell, pos, false).execute(playerPtr);
}
ESM::RefId cellId = world->findCellPosition(cell, pos);
MWWorld::ActionTeleport(cellId, pos, false).execute(playerPtr);
world->adjustPosition(playerPtr, false);
}
};

View file

@ -19,9 +19,9 @@
namespace MWWorld
{
ActionTeleport::ActionTeleport(std::string_view cellName, const ESM::Position& position, bool teleportFollowers)
ActionTeleport::ActionTeleport(ESM::RefId cellId, const ESM::Position& position, bool teleportFollowers)
: Action(true)
, mCellName(cellName)
, mCellId(cellId)
, mPosition(position)
, mTeleportFollowers(teleportFollowers)
{
@ -33,7 +33,9 @@ namespace MWWorld
{
// Find any NPCs that are following the actor and teleport them with him
std::set<MWWorld::Ptr> followers;
getFollowers(actor, followers, mCellName.empty(), true);
bool toExterior = MWBase::Environment::get().getWorldModel()->getCell(mCellId)->isExterior();
getFollowers(actor, followers, toExterior, true);
for (std::set<MWWorld::Ptr>::iterator it = followers.begin(); it != followers.end(); ++it)
teleport(*it);
@ -52,10 +54,8 @@ namespace MWWorld
if (actor == world->getPlayerPtr())
{
world->getPlayer().setTeleported(true);
if (mCellName.empty())
world->changeToExteriorCell(mPosition, true);
else
world->changeToInteriorCell(mCellName, mPosition, true);
if (!mCellId.empty())
world->changeToCell(mCellId, mPosition, true);
teleported = world->getPlayerPtr();
}
else
@ -65,15 +65,9 @@ namespace MWWorld
actor.getClass().getCreatureStats(actor).getAiSequence().stopCombat();
return;
}
else if (mCellName.empty())
{
const osg::Vec2i index = positionToCellIndex(mPosition.pos[0], mPosition.pos[1]);
teleported = world->moveObject(
actor, worldModel->getExterior(index.x(), index.y()), mPosition.asVec3(), true, true);
}
else
teleported
= world->moveObject(actor, worldModel->getInterior(mCellName), mPosition.asVec3(), true, true);
teleported = world->moveObject(actor, worldModel->getCell(mCellId), mPosition.asVec3(), true, true);
}
if (!world->isWaterWalkingCastableOnTarget(teleported) && MWMechanics::hasWaterWalking(teleported))

View file

@ -13,7 +13,7 @@ namespace MWWorld
{
class ActionTeleport : public Action
{
std::string mCellName;
ESM::RefId mCellId;
ESM::Position mPosition;
bool mTeleportFollowers;
@ -26,7 +26,7 @@ namespace MWWorld
public:
/// If cellName is empty, an exterior cell is assumed.
/// @param teleportFollowers Whether to teleport any following actors of the target actor as well.
ActionTeleport(std::string_view cellName, const ESM::Position& position, bool teleportFollowers);
ActionTeleport(ESM::RefId cellId, const ESM::Position& position, bool teleportFollowers);
/// @param includeHostiles If true, include hostile followers (which won't actually be teleported) in the
/// output,

View file

@ -3,8 +3,11 @@
#include <cassert>
#include <components/debug/debuglog.hpp>
#include <components/esm3/cellid.hpp>
#include <components/esm3/objectstate.hpp>
#include <apps/openmw/mwworld/cellutils.hpp>
namespace MWWorld
{
CellRef::CellRef(const ESM::CellRef& ref)
@ -67,12 +70,54 @@ namespace MWWorld
static const std::string emptyString = "";
const std::string& CellRef::getDestCell() const
ESM::Position CellRef::getDoorDest() const
{
return std::visit(ESM::VisitOverload{
[&](const ESM4::Reference& /*ref*/) -> const std::string& { return emptyString; },
[&](const ESM::CellRef& ref) -> const std::string& { return ref.mDestCell; },
},
auto esm3Visit = [&](const ESM::CellRef& ref) -> ESM::Position {
// So the destinaion pos is always in relationship to the destination cells origin, interior or exterior
// alike
ESM::Position pos = ref.mDoorDest;
if (ref.mDestCell.empty()) // Exterior cell case
{
const osg::Vec2i index = positionToCellIndex(ref.mDoorDest.pos[0], ref.mDoorDest.pos[1]);
pos.pos[0] -= index.x() * Constants::CellSizeInUnits;
pos.pos[1] -= index.y() * Constants::CellSizeInUnits;
}
return pos;
};
return std::visit(
ESM::VisitOverload{
[&](const ESM4::Reference& ref) { return ref.mDoor.destPos; },
esm3Visit,
},
mCellRef.mVariant);
}
ESM::RefId CellRef::getDestCell() const
{
auto esm3Visit = [&](const ESM::CellRef& ref) -> ESM::RefId {
if (!ref.mDestCell.empty())
{
return ESM::RefId::stringRefId(ref.mDestCell);
}
else
{
const osg::Vec2i index = positionToCellIndex(ref.mDoorDest.pos[0], ref.mDoorDest.pos[1]);
ESM::CellId CellId;
CellId.mPaged = true;
CellId.mIndex.mX = index.x();
CellId.mIndex.mY = index.y();
return CellId.getCellRefId();
}
};
return std::visit(
ESM::VisitOverload{
[&](const ESM4::Reference& ref) -> ESM::RefId { return ESM::RefId::sEmpty; },
esm3Visit,
},
mCellRef.mVariant);
}

View file

@ -61,18 +61,10 @@ namespace MWWorld
}
// Teleport location for the door, if this is a teleporting door.
const ESM::Position& getDoorDest() const
{
struct Visitor
{
const ESM::Position& operator()(const ESM::CellRef& ref) { return ref.mDoorDest; }
const ESM::Position& operator()(const ESM4::Reference& ref) { return ref.mDoor.destPos; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
ESM::Position getDoorDest() const;
// Destination cell for doors (optional)
const std::string& getDestCell() const;
ESM::RefId getDestCell() const;
// Scale applied to mesh
float getScale() const

View file

@ -1081,7 +1081,7 @@ namespace MWWorld
ESM::CellId movedTo;
refnum.load(reader, true, "MVRF");
movedTo.load(reader);
ESM::RefId movedToId = movedTo.getCellRefId();
if (refnum.hasContentFile())
{
auto iter = contentFileMap.find(refnum.mContentFile);
@ -1098,7 +1098,7 @@ namespace MWWorld
continue;
}
CellStore* otherCell = callback->getCellStore(movedTo);
CellStore* otherCell = callback->getCellStore(movedToId);
if (otherCell == nullptr)
{

View file

@ -291,7 +291,7 @@ namespace MWWorld
struct GetCellStoreCallback
{
///@note must return nullptr if the cell is not found
virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0;
virtual CellStore* getCellStore(const ESM::RefId& cellId) = 0;
virtual ~GetCellStoreCallback() = default;
};

View file

@ -367,7 +367,7 @@ namespace MWWorld
try
{
mCellStore = MWBase::Environment::get().getWorldModel()->getCell(player.mCellId);
mCellStore = MWBase::Environment::get().getWorldModel()->getCellFromCellId(player.mCellId);
}
catch (...)
{
@ -404,7 +404,7 @@ namespace MWWorld
if (player.mHasMark)
{
mMarkedPosition = player.mMarkedPosition;
mMarkedCell = MWBase::Environment::get().getWorldModel()->getCell(player.mMarkedCell);
mMarkedCell = MWBase::Environment::get().getWorldModel()->getCellFromCellId(player.mMarkedCell);
}
else
{

View file

@ -1129,15 +1129,7 @@ namespace MWWorld
{
try
{
if (!door.getCellRef().getDestCell().empty())
preloadCell(mWorld.getWorldModel().getInterior(door.getCellRef().getDestCell()));
else
{
osg::Vec3f pos = door.getCellRef().getDoorDest().asVec3();
const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y());
preloadCell(mWorld.getWorldModel().getExterior(cellIndex.x(), cellIndex.y()), true);
exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
}
preloadCell(mWorld.getWorldModel().getCell(door.getCellRef().getDestCell()));
}
catch (std::exception&)
{

View file

@ -471,7 +471,7 @@ namespace MWWorld
const ESM::Cell* Store<ESM::Cell>::search(const ESM::RefId& cellId) const
{
auto foundCellIt = mCells.find(cellId);
if (foundCellIt == mCells.end())
if (foundCellIt != mCells.end())
return &foundCellIt->second;
return nullptr;
}

View file

@ -353,7 +353,7 @@ namespace MWWorld
if (bypass && !mStartCell.empty())
{
ESM::Position pos;
if (findExteriorPosition(mStartCell, pos))
if (findExteriorPosition(mStartCell, pos).empty())
{
changeToExteriorCell(pos, true);
adjustPosition(getPlayerPtr(), false);
@ -1000,6 +1000,31 @@ namespace MWWorld
mCurrentDate->setup(mGlobalVariables);
}
void World::changeToCell(
const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
{
const MWWorld::Cell* destinationCell = getWorldModel().getCell(cellId)->getCell();
bool exteriorCell = destinationCell->isExterior();
mPhysics->clearQueuedMovement();
mDiscardMovements = true;
if (changeEvent && mCurrentWorldSpace != destinationCell->getNameId())
{
// changed worldspace
mProjectileManager->clear();
mRendering->notifyWorldSpaceChanged();
mCurrentWorldSpace = destinationCell->getNameId();
}
removeContainerScripts(getPlayerPtr());
if (exteriorCell)
mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent);
else
mWorldScene->changeToInteriorCell(destinationCell->getNameId(), position, adjustPlayerPos, changeEvent);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
mRendering->getCamera()->instantTransition();
}
float World::getMaxActivationDistance() const
{
if (mActivationDistanceOverride >= 0)
@ -1420,9 +1445,9 @@ namespace MWWorld
esmPos.pos[0] = traced.x();
esmPos.pos[1] = traced.y();
esmPos.pos[2] = traced.z();
std::string_view cell;
ESM::RefId cell;
if (!actor.getCell()->isExterior())
cell = actor.getCell()->getCell()->getNameId();
cell = actor.getCell()->getCell()->getId();
MWWorld::ActionTeleport(cell, esmPos, false).execute(actor);
}
}
@ -2060,24 +2085,7 @@ namespace MWWorld
{
World::DoorMarker newMarker;
newMarker.name = MWClass::Door::getDestination(ref);
ESM::CellId cellid;
if (!ref.mRef.getDestCell().empty())
{
cellid.mWorldspace = ref.mRef.getDestCell();
cellid.mPaged = false;
cellid.mIndex.mX = 0;
cellid.mIndex.mY = 0;
}
else
{
cellid.mPaged = true;
const osg::Vec2i index
= positionToCellIndex(ref.mRef.getDoorDest().pos[0], ref.mRef.getDoorDest().pos[1]);
cellid.mIndex.mX = index.x();
cellid.mIndex.mY = index.y();
}
newMarker.dest = cellid;
newMarker.dest = ref.mRef.getDestCell();
ESM::Position pos = ref.mData.getPosition();
@ -2764,7 +2772,7 @@ namespace MWWorld
physicActor->enableCollisionBody(enable);
}
bool World::findInteriorPosition(std::string_view name, ESM::Position& pos)
ESM::RefId World::findInteriorPosition(std::string_view name, ESM::Position& pos)
{
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
@ -2772,8 +2780,9 @@ namespace MWWorld
MWWorld::CellStore* cellStore = mWorldModel.getInterior(name);
if (!cellStore)
return false;
return ESM::RefId::sEmpty;
ESM::RefId cellId = cellStore->getCell()->getId();
std::vector<const MWWorld::CellRef*> sortedDoors;
for (const MWWorld::LiveCellRef<ESM::Door>& door : cellStore->getReadOnlyDoors().mList)
{
@ -2794,32 +2803,21 @@ namespace MWWorld
for (const MWWorld::CellRef* door : sortedDoors)
{
MWWorld::CellStore* source = nullptr;
source = mWorldModel.getCell(door->getDestCell());
// door to exterior
if (door->getDestCell().empty())
{
ESM::Position doorDest = door->getDoorDest();
const osg::Vec2i index = positionToCellIndex(doorDest.pos[0], doorDest.pos[1]);
source = mWorldModel.getExterior(index.x(), index.y());
}
// door to interior
else
{
source = mWorldModel.getInterior(door->getDestCell());
}
if (source)
{
// Find door leading to our current teleport door
// and use its destination to position inside cell.
for (const MWWorld::LiveCellRef<ESM::Door>& destDoor : source->getReadOnlyDoors().mList)
{
if (name == destDoor.mRef.getDestCell())
if (ESM::RefId::stringRefId(name) == destDoor.mRef.getDestCell())
{
/// \note Using _any_ door pointed to the interior,
/// not the one pointed to current door.
pos = destDoor.mRef.getDoorDest();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true;
return cellId;
}
}
}
@ -2831,7 +2829,7 @@ namespace MWWorld
// found the COC position?
pos = stat4.mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true;
return cellId;
}
}
// Fall back to the first static location.
@ -2840,7 +2838,7 @@ namespace MWWorld
{
pos = statics4.begin()->mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true;
return cellId;
}
// Fall back to the first static location.
const MWWorld::CellRefList<ESM::Static>::List& statics = cellStore->getReadOnlyStatics().mList;
@ -2848,13 +2846,24 @@ namespace MWWorld
{
pos = statics.begin()->mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true;
return cellId;
}
return false;
return ESM::RefId::sEmpty;
}
bool World::findExteriorPosition(std::string_view nameId, ESM::Position& pos)
ESM::RefId World::findCellPosition(std::string_view cellName, ESM::Position& pos)
{
ESM::RefId foundCell = findInteriorPosition(cellName, pos);
if (foundCell.empty())
{
return findInteriorPosition(cellName, pos);
}
return foundCell;
}
ESM::RefId World::findExteriorPosition(std::string_view nameId, ESM::Position& pos)
{
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
@ -2863,7 +2872,7 @@ namespace MWWorld
{
ext = mWorldModel.getCell(nameId)->getCell();
if (!ext->isExterior())
return false;
return ESM::RefId::sEmpty;
}
catch (std::exception&)
{
@ -2895,10 +2904,10 @@ namespace MWWorld
// Note: Z pos will be adjusted by adjustPosition later
pos.pos[2] = 0;
return true;
return ext->getId();
}
return false;
return ESM::RefId::sEmpty;
}
void World::enableTeleporting(bool enable)
@ -3289,10 +3298,10 @@ namespace MWWorld
// 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_view> checkedCells;
std::set<std::string_view> currentCells;
std::set<std::string_view> nextCells;
nextCells.insert(cell->getCell()->getNameId());
std::set<ESM::RefId> checkedCells;
std::set<ESM::RefId> currentCells;
std::set<ESM::RefId> nextCells;
nextCells.insert(cell->getCell()->getId());
while (!nextCells.empty())
{
@ -3300,7 +3309,7 @@ namespace MWWorld
nextCells.clear();
for (const auto& currentCell : currentCells)
{
MWWorld::CellStore* next = mWorldModel.getInterior(currentCell);
MWWorld::CellStore* next = mWorldModel.getCell(currentCell);
if (!next)
continue;
@ -3318,7 +3327,7 @@ namespace MWWorld
}
else
{
const std::string_view dest = ref.mRef.getDestCell();
ESM::RefId dest = ref.mRef.getDestCell();
if (!checkedCells.count(dest) && !currentCells.count(dest))
nextCells.insert(dest);
}
@ -3342,19 +3351,19 @@ namespace MWWorld
// 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_view> checkedCells;
std::set<std::string_view> currentCells;
std::set<std::string_view> nextCells;
std::set<ESM::RefId> checkedCells;
std::set<ESM::RefId> currentCells;
std::set<ESM::RefId> nextCells;
MWWorld::ConstPtr closestMarker;
nextCells.insert(ptr.getCell()->getCell()->getNameId());
nextCells.insert(ptr.getCell()->getCell()->getId());
while (!nextCells.empty())
{
currentCells = nextCells;
nextCells.clear();
for (const auto& cell : currentCells)
{
MWWorld::CellStore* next = mWorldModel.getInterior(cell);
MWWorld::CellStore* next = mWorldModel.getCell(cell);
checkedCells.insert(cell);
if (!next)
continue;
@ -3440,11 +3449,11 @@ namespace MWWorld
return;
}
std::string_view cellName = "";
ESM::RefId cellId;
if (!closestMarker.mCell->isExterior())
cellName = closestMarker.mCell->getCell()->getNameId();
cellId = closestMarker.mCell->getCell()->getId();
MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false);
MWWorld::ActionTeleport action(cellId, closestMarker.getRefData().getPosition(), false);
action.execute(ptr);
}
@ -3646,13 +3655,13 @@ namespace MWWorld
Log(Debug::Warning) << "Failed to confiscate items: no closest prison marker found.";
return;
}
std::string_view prisonName = prisonMarker.getCellRef().getDestCell();
ESM::RefId prisonName = prisonMarker.getCellRef().getDestCell();
if (prisonName.empty())
{
Log(Debug::Warning) << "Failed to confiscate items: prison marker not linked to prison interior";
return;
}
MWWorld::CellStore* prison = mWorldModel.getInterior(prisonName);
MWWorld::CellStore* prison = mWorldModel.getCell(prisonName);
if (!prison)
{
Log(Debug::Warning) << "Failed to confiscate items: failed to load cell " << prisonName;

View file

@ -351,6 +351,9 @@ namespace MWWorld
void changeToCell(const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos,
bool changeEvent = true) override;
void changeToCell(const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos,
bool changeEvent = true) override;
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
MWWorld::Ptr getFacedObject() override;
@ -601,12 +604,12 @@ namespace MWWorld
/// Find center of exterior cell above land surface
/// \return false if exterior with given name not exists, true otherwise
bool findExteriorPosition(std::string_view nameId, ESM::Position& pos) override;
ESM::RefId findExteriorPosition(std::string_view nameId, ESM::Position& pos) override;
/// Find position in interior cell near door entrance
/// \return false if interior with given name not exists, true otherwise
bool findInteriorPosition(std::string_view name, ESM::Position& pos) override;
ESM::RefId findInteriorPosition(std::string_view name, ESM::Position& pos) override;
ESM::RefId findCellPosition(std::string_view cellName, ESM::Position& pos) override;
/// Enables or disables use of teleport spell effects (recall, intervention, etc).
void enableTeleporting(bool enable) override;

View file

@ -226,7 +226,7 @@ MWWorld::CellStore* MWWorld::WorldModel::getInterior(std::string_view name)
return result->second;
}
MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::CellId& id)
MWWorld::CellStore* MWWorld::WorldModel::getCellFromCellId(const ESM::CellId& id)
{
if (id.mPaged)
return getExterior(id.mIndex.mX, id.mIndex.mY);
@ -234,6 +234,52 @@ MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::CellId& id)
return getInterior(id.mWorldspace);
}
MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::RefId& id)
{
auto result = mCells.find(id);
if (result != mCells.end())
return &result->second;
// TODO: in the future replace that with elsid's refId variant that can be a osg::Vec2i
const std::string& idString = id.getRefIdString();
if (idString[0] == '#' && idString.find(',')) // That is an exterior cell Id
{
int x, y;
std::stringstream stringStream = std::stringstream(idString);
char sharp = '#';
char comma = ',';
stringStream >> sharp >> x >> comma >> y;
return getExterior(x, y);
}
const ESM4::Cell* cell4 = mStore.get<ESM4::Cell>().search(id);
CellStore* newCellStore = nullptr;
if (!cell4)
{
const ESM::Cell* cell = mStore.get<ESM::Cell>().search(id);
newCellStore = &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
}
else
{
newCellStore = &mCells.emplace(cell4->mId, CellStore(MWWorld::Cell(*cell4), mStore, mReaders)).first->second;
}
if (newCellStore->getCell()->isExterior())
{
std::pair<int, int> coord
= std::make_pair(newCellStore->getCell()->getGridX(), newCellStore->getCell()->getGridY());
mExteriors.emplace(coord, newCellStore).first;
}
else
{
mInteriors.emplace(newCellStore->getCell()->getNameId(), newCellStore).first;
}
if (newCellStore->getState() != CellStore::State_Loaded)
{
newCellStore->load();
}
return newCellStore;
}
const ESM::Cell* MWWorld::WorldModel::getESMCellByName(std::string_view name)
{
const ESM::Cell* cell = mStore.get<ESM::Cell>().search(name); // first try interiors
@ -428,7 +474,7 @@ public:
MWWorld::WorldModel& mWorldModel;
MWWorld::CellStore* getCellStore(const ESM::CellId& cellId) override
MWWorld::CellStore* getCellStore(const ESM::RefId& cellId) override
{
try
{
@ -452,7 +498,7 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, cons
try
{
cellStore = getCell(state.mId);
cellStore = getCell(state.mId.getCellRefId());
}
catch (...)
{

View file

@ -70,7 +70,8 @@ namespace MWWorld
CellStore* getExterior(int x, int y);
CellStore* getInterior(std::string_view name);
CellStore* getCell(std::string_view name); // interior or named exterior
CellStore* getCell(const ESM::CellId& Id);
CellStore* getCell(const ESM::RefId& Id);
CellStore* getCellFromCellId(const ESM::CellId& Id);
// If cellNameInSameWorldSpace is an interior - returns this interior.
// Otherwise returns exterior cell for given position in the same world space.

View file

@ -33,6 +33,18 @@ namespace ESM
esm.writeHNT("CIDX", mIndex, 8);
}
ESM::RefId CellId::getCellRefId() const
{
if (mPaged)
{
return ESM::RefId::stringRefId("#" + std::to_string(mIndex.mX) + "," + std::to_string(mIndex.mY));
}
else
{
return ESM::RefId::stringRefId(mWorldspace);
}
}
bool operator==(const CellId& left, const CellId& right)
{
return left.mWorldspace == right.mWorldspace && left.mPaged == right.mPaged

View file

@ -25,6 +25,7 @@ namespace ESM
void load(ESMReader& esm);
void save(ESMWriter& esm) const;
ESM::RefId getCellRefId() const;
};
bool operator==(const CellId& left, const CellId& right);

View file

@ -10,7 +10,7 @@ namespace ESM
{
esm.writeHNT("POSX", mWorldX);
esm.writeHNT("POSY", mWorldY);
mCell.save(esm);
mCellId.save(esm);
if (!mNote.empty())
esm.writeHNString("NOTE", mNote);
}
@ -19,7 +19,8 @@ namespace ESM
{
esm.getHNT(mWorldX, "POSX");
esm.getHNT(mWorldY, "POSY");
mCell.load(esm);
mCellId.load(esm);
mCell = mCellId.getCellRefId();
mNote = esm.getHNOString("NOTE");
}

View file

@ -12,7 +12,8 @@ namespace ESM
float mWorldX;
float mWorldY;
CellId mCell;
RefId mCell;
CellId mCellId; // The CellId representation for saving/loading
std::string mNote;

View file

@ -59,14 +59,7 @@ namespace ESM
const ESM::RefId& Cell::updateId()
{
if (isExterior())
{
mId = ESM::RefId::stringRefId("#" + std::to_string(mData.mX) + "," + std::to_string(mData.mY));
}
else
{
mId = ESM::RefId::stringRefId(mName);
}
mId = mCellId.getCellRefId();
return mId;
}