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

View file

@ -23,6 +23,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/failedaction.hpp" #include "../mwworld/failedaction.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/worldmodel.hpp"
#include "../mwgui/tooltips.hpp" #include "../mwgui/tooltips.hpp"
#include "../mwgui/ustring.hpp" #include "../mwgui/ustring.hpp"
@ -298,16 +299,8 @@ namespace MWClass
std::string Door::getDestination(const MWWorld::LiveCellRef<ESM::Door>& door) std::string Door::getDestination(const MWWorld::LiveCellRef<ESM::Door>& door)
{ {
std::string_view dest = door.mRef.getDestCell(); std::string_view dest
if (dest.empty()) = MWBase::Environment::get().getWorldModel()->getCell(door.mRef.getDestCell())->getCell()->getDisplayName();
{
// 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);
}
return "#{sCell=" + std::string{ dest } + "}"; return "#{sCell=" + std::string{ dest } + "}";
} }

View file

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

View file

@ -56,14 +56,14 @@ namespace MWGui
size_t size() const; 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; typedef std::pair<ContainerType::const_iterator, ContainerType::const_iterator> RangeType;
ContainerType::const_iterator begin() const; ContainerType::const_iterator begin() const;
ContainerType::const_iterator end() 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; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
EventHandle_Void eventMarkersChanged; EventHandle_Void eventMarkersChanged;

View file

@ -195,9 +195,15 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
MWBase::Environment::get().getWindowManager()->fadeScreenOut(1); 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. // Teleports any followers, too.
MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); MWWorld::ActionTeleport action(cellId.getCellRefId(), pos, true);
action.execute(player); action.execute(player);
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);

View file

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

View file

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

View file

@ -92,20 +92,10 @@ namespace MWScript
ESM::Position pos; ESM::Position pos;
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Ptr playerPtr = world->getPlayerPtr(); const MWWorld::Ptr playerPtr = world->getPlayerPtr();
ESM::RefId cellId = world->findCellPosition(cell, pos);
if (world->findExteriorPosition(cell, pos)) MWWorld::ActionTeleport(cellId, pos, false).execute(playerPtr);
{
MWWorld::ActionTeleport({}, pos, false).execute(playerPtr);
world->adjustPosition(playerPtr, false); 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);
}
}
}; };
class OpCOE : public Interpreter::Opcode0 class OpCOE : public Interpreter::Opcode0

View file

@ -19,9 +19,9 @@
namespace MWWorld 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) : Action(true)
, mCellName(cellName) , mCellId(cellId)
, mPosition(position) , mPosition(position)
, mTeleportFollowers(teleportFollowers) , mTeleportFollowers(teleportFollowers)
{ {
@ -33,7 +33,9 @@ namespace MWWorld
{ {
// Find any NPCs that are following the actor and teleport them with him // Find any NPCs that are following the actor and teleport them with him
std::set<MWWorld::Ptr> followers; 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) for (std::set<MWWorld::Ptr>::iterator it = followers.begin(); it != followers.end(); ++it)
teleport(*it); teleport(*it);
@ -52,10 +54,8 @@ namespace MWWorld
if (actor == world->getPlayerPtr()) if (actor == world->getPlayerPtr())
{ {
world->getPlayer().setTeleported(true); world->getPlayer().setTeleported(true);
if (mCellName.empty()) if (!mCellId.empty())
world->changeToExteriorCell(mPosition, true); world->changeToCell(mCellId, mPosition, true);
else
world->changeToInteriorCell(mCellName, mPosition, true);
teleported = world->getPlayerPtr(); teleported = world->getPlayerPtr();
} }
else else
@ -65,15 +65,9 @@ namespace MWWorld
actor.getClass().getCreatureStats(actor).getAiSequence().stopCombat(); actor.getClass().getCreatureStats(actor).getAiSequence().stopCombat();
return; 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 else
teleported teleported = world->moveObject(actor, worldModel->getCell(mCellId), mPosition.asVec3(), true, true);
= world->moveObject(actor, worldModel->getInterior(mCellName), mPosition.asVec3(), true, true);
} }
if (!world->isWaterWalkingCastableOnTarget(teleported) && MWMechanics::hasWaterWalking(teleported)) if (!world->isWaterWalkingCastableOnTarget(teleported) && MWMechanics::hasWaterWalking(teleported))

View file

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

View file

@ -3,8 +3,11 @@
#include <cassert> #include <cassert>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/esm3/cellid.hpp>
#include <components/esm3/objectstate.hpp> #include <components/esm3/objectstate.hpp>
#include <apps/openmw/mwworld/cellutils.hpp>
namespace MWWorld namespace MWWorld
{ {
CellRef::CellRef(const ESM::CellRef& ref) CellRef::CellRef(const ESM::CellRef& ref)
@ -67,11 +70,53 @@ namespace MWWorld
static const std::string emptyString = ""; 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; }, auto esm3Visit = [&](const ESM::CellRef& ref) -> ESM::Position {
[&](const ESM::CellRef& ref) -> const std::string& { return ref.mDestCell; }, // 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); mCellRef.mVariant);
} }

View file

@ -61,18 +61,10 @@ namespace MWWorld
} }
// Teleport location for the door, if this is a teleporting door. // Teleport location for the door, if this is a teleporting door.
const ESM::Position& getDoorDest() 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);
}
// Destination cell for doors (optional) // Destination cell for doors (optional)
const std::string& getDestCell() const; ESM::RefId getDestCell() const;
// Scale applied to mesh // Scale applied to mesh
float getScale() const float getScale() const

View file

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

View file

@ -291,7 +291,7 @@ namespace MWWorld
struct GetCellStoreCallback struct GetCellStoreCallback
{ {
///@note must return nullptr if the cell is not found ///@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; virtual ~GetCellStoreCallback() = default;
}; };

View file

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

View file

@ -1129,15 +1129,7 @@ namespace MWWorld
{ {
try try
{ {
if (!door.getCellRef().getDestCell().empty()) preloadCell(mWorld.getWorldModel().getCell(door.getCellRef().getDestCell()));
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)));
}
} }
catch (std::exception&) catch (std::exception&)
{ {

View file

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

View file

@ -353,7 +353,7 @@ namespace MWWorld
if (bypass && !mStartCell.empty()) if (bypass && !mStartCell.empty())
{ {
ESM::Position pos; ESM::Position pos;
if (findExteriorPosition(mStartCell, pos)) if (findExteriorPosition(mStartCell, pos).empty())
{ {
changeToExteriorCell(pos, true); changeToExteriorCell(pos, true);
adjustPosition(getPlayerPtr(), false); adjustPosition(getPlayerPtr(), false);
@ -1000,6 +1000,31 @@ namespace MWWorld
mCurrentDate->setup(mGlobalVariables); 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 float World::getMaxActivationDistance() const
{ {
if (mActivationDistanceOverride >= 0) if (mActivationDistanceOverride >= 0)
@ -1420,9 +1445,9 @@ namespace MWWorld
esmPos.pos[0] = traced.x(); esmPos.pos[0] = traced.x();
esmPos.pos[1] = traced.y(); esmPos.pos[1] = traced.y();
esmPos.pos[2] = traced.z(); esmPos.pos[2] = traced.z();
std::string_view cell; ESM::RefId cell;
if (!actor.getCell()->isExterior()) if (!actor.getCell()->isExterior())
cell = actor.getCell()->getCell()->getNameId(); cell = actor.getCell()->getCell()->getId();
MWWorld::ActionTeleport(cell, esmPos, false).execute(actor); MWWorld::ActionTeleport(cell, esmPos, false).execute(actor);
} }
} }
@ -2060,24 +2085,7 @@ namespace MWWorld
{ {
World::DoorMarker newMarker; World::DoorMarker newMarker;
newMarker.name = MWClass::Door::getDestination(ref); newMarker.name = MWClass::Door::getDestination(ref);
newMarker.dest = ref.mRef.getDestCell();
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;
ESM::Position pos = ref.mData.getPosition(); ESM::Position pos = ref.mData.getPosition();
@ -2764,7 +2772,7 @@ namespace MWWorld
physicActor->enableCollisionBody(enable); 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.rot[0] = pos.rot[1] = pos.rot[2] = 0;
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
@ -2772,8 +2780,9 @@ namespace MWWorld
MWWorld::CellStore* cellStore = mWorldModel.getInterior(name); MWWorld::CellStore* cellStore = mWorldModel.getInterior(name);
if (!cellStore) if (!cellStore)
return false; return ESM::RefId::sEmpty;
ESM::RefId cellId = cellStore->getCell()->getId();
std::vector<const MWWorld::CellRef*> sortedDoors; std::vector<const MWWorld::CellRef*> sortedDoors;
for (const MWWorld::LiveCellRef<ESM::Door>& door : cellStore->getReadOnlyDoors().mList) for (const MWWorld::LiveCellRef<ESM::Door>& door : cellStore->getReadOnlyDoors().mList)
{ {
@ -2794,32 +2803,21 @@ namespace MWWorld
for (const MWWorld::CellRef* door : sortedDoors) for (const MWWorld::CellRef* door : sortedDoors)
{ {
MWWorld::CellStore* source = nullptr; 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) if (source)
{ {
// Find door leading to our current teleport door // Find door leading to our current teleport door
// and use its destination to position inside cell. // and use its destination to position inside cell.
for (const MWWorld::LiveCellRef<ESM::Door>& destDoor : source->getReadOnlyDoors().mList) 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, /// \note Using _any_ door pointed to the interior,
/// not the one pointed to current door. /// not the one pointed to current door.
pos = destDoor.mRef.getDoorDest(); pos = destDoor.mRef.getDoorDest();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true; return cellId;
} }
} }
} }
@ -2831,7 +2829,7 @@ namespace MWWorld
// found the COC position? // found the COC position?
pos = stat4.mRef.getPosition(); pos = stat4.mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true; return cellId;
} }
} }
// Fall back to the first static location. // Fall back to the first static location.
@ -2840,7 +2838,7 @@ namespace MWWorld
{ {
pos = statics4.begin()->mRef.getPosition(); pos = statics4.begin()->mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true; return cellId;
} }
// Fall back to the first static location. // Fall back to the first static location.
const MWWorld::CellRefList<ESM::Static>::List& statics = cellStore->getReadOnlyStatics().mList; const MWWorld::CellRefList<ESM::Static>::List& statics = cellStore->getReadOnlyStatics().mList;
@ -2848,13 +2846,24 @@ namespace MWWorld
{ {
pos = statics.begin()->mRef.getPosition(); pos = statics.begin()->mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; 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; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
@ -2863,7 +2872,7 @@ namespace MWWorld
{ {
ext = mWorldModel.getCell(nameId)->getCell(); ext = mWorldModel.getCell(nameId)->getCell();
if (!ext->isExterior()) if (!ext->isExterior())
return false; return ESM::RefId::sEmpty;
} }
catch (std::exception&) catch (std::exception&)
{ {
@ -2895,10 +2904,10 @@ namespace MWWorld
// Note: Z pos will be adjusted by adjustPosition later // Note: Z pos will be adjusted by adjustPosition later
pos.pos[2] = 0; pos.pos[2] = 0;
return true; return ext->getId();
} }
return false; return ESM::RefId::sEmpty;
} }
void World::enableTeleporting(bool enable) void World::enableTeleporting(bool enable)
@ -3289,10 +3298,10 @@ namespace MWWorld
// Search for a 'nearest' exterior, counting each cell between the starting // 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. // cell and the exterior as a distance of 1. Will fail for isolated interiors.
std::set<std::string_view> checkedCells; std::set<ESM::RefId> checkedCells;
std::set<std::string_view> currentCells; std::set<ESM::RefId> currentCells;
std::set<std::string_view> nextCells; std::set<ESM::RefId> nextCells;
nextCells.insert(cell->getCell()->getNameId()); nextCells.insert(cell->getCell()->getId());
while (!nextCells.empty()) while (!nextCells.empty())
{ {
@ -3300,7 +3309,7 @@ namespace MWWorld
nextCells.clear(); nextCells.clear();
for (const auto& currentCell : currentCells) for (const auto& currentCell : currentCells)
{ {
MWWorld::CellStore* next = mWorldModel.getInterior(currentCell); MWWorld::CellStore* next = mWorldModel.getCell(currentCell);
if (!next) if (!next)
continue; continue;
@ -3318,7 +3327,7 @@ namespace MWWorld
} }
else else
{ {
const std::string_view dest = ref.mRef.getDestCell(); ESM::RefId dest = ref.mRef.getDestCell();
if (!checkedCells.count(dest) && !currentCells.count(dest)) if (!checkedCells.count(dest) && !currentCells.count(dest))
nextCells.insert(dest); nextCells.insert(dest);
} }
@ -3342,19 +3351,19 @@ namespace MWWorld
// Search for a 'nearest' marker, counting each cell between the starting // 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 // cell and the exterior as a distance of 1. If an exterior is found, jump
// to the nearest exterior marker, without further interior searching. // to the nearest exterior marker, without further interior searching.
std::set<std::string_view> checkedCells; std::set<ESM::RefId> checkedCells;
std::set<std::string_view> currentCells; std::set<ESM::RefId> currentCells;
std::set<std::string_view> nextCells; std::set<ESM::RefId> nextCells;
MWWorld::ConstPtr closestMarker; MWWorld::ConstPtr closestMarker;
nextCells.insert(ptr.getCell()->getCell()->getNameId()); nextCells.insert(ptr.getCell()->getCell()->getId());
while (!nextCells.empty()) while (!nextCells.empty())
{ {
currentCells = nextCells; currentCells = nextCells;
nextCells.clear(); nextCells.clear();
for (const auto& cell : currentCells) for (const auto& cell : currentCells)
{ {
MWWorld::CellStore* next = mWorldModel.getInterior(cell); MWWorld::CellStore* next = mWorldModel.getCell(cell);
checkedCells.insert(cell); checkedCells.insert(cell);
if (!next) if (!next)
continue; continue;
@ -3440,11 +3449,11 @@ namespace MWWorld
return; return;
} }
std::string_view cellName = ""; ESM::RefId cellId;
if (!closestMarker.mCell->isExterior()) 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); action.execute(ptr);
} }
@ -3646,13 +3655,13 @@ namespace MWWorld
Log(Debug::Warning) << "Failed to confiscate items: no closest prison marker found."; Log(Debug::Warning) << "Failed to confiscate items: no closest prison marker found.";
return; return;
} }
std::string_view prisonName = prisonMarker.getCellRef().getDestCell(); ESM::RefId prisonName = prisonMarker.getCellRef().getDestCell();
if (prisonName.empty()) if (prisonName.empty())
{ {
Log(Debug::Warning) << "Failed to confiscate items: prison marker not linked to prison interior"; Log(Debug::Warning) << "Failed to confiscate items: prison marker not linked to prison interior";
return; return;
} }
MWWorld::CellStore* prison = mWorldModel.getInterior(prisonName); MWWorld::CellStore* prison = mWorldModel.getCell(prisonName);
if (!prison) if (!prison)
{ {
Log(Debug::Warning) << "Failed to confiscate items: failed to load cell " << prisonName; 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, void changeToCell(const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos,
bool changeEvent = true) override; 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 ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
MWWorld::Ptr getFacedObject() override; MWWorld::Ptr getFacedObject() override;
@ -601,12 +604,12 @@ namespace MWWorld
/// Find center of exterior cell above land surface /// Find center of exterior cell above land surface
/// \return false if exterior with given name not exists, true otherwise /// \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 /// Find position in interior cell near door entrance
/// \return false if interior with given name not exists, true otherwise /// \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). /// Enables or disables use of teleport spell effects (recall, intervention, etc).
void enableTeleporting(bool enable) override; void enableTeleporting(bool enable) override;

View file

@ -226,7 +226,7 @@ MWWorld::CellStore* MWWorld::WorldModel::getInterior(std::string_view name)
return result->second; return result->second;
} }
MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::CellId& id) MWWorld::CellStore* MWWorld::WorldModel::getCellFromCellId(const ESM::CellId& id)
{ {
if (id.mPaged) if (id.mPaged)
return getExterior(id.mIndex.mX, id.mIndex.mY); 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); 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* MWWorld::WorldModel::getESMCellByName(std::string_view name)
{ {
const ESM::Cell* cell = mStore.get<ESM::Cell>().search(name); // first try interiors const ESM::Cell* cell = mStore.get<ESM::Cell>().search(name); // first try interiors
@ -428,7 +474,7 @@ public:
MWWorld::WorldModel& mWorldModel; MWWorld::WorldModel& mWorldModel;
MWWorld::CellStore* getCellStore(const ESM::CellId& cellId) override MWWorld::CellStore* getCellStore(const ESM::RefId& cellId) override
{ {
try try
{ {
@ -452,7 +498,7 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, cons
try try
{ {
cellStore = getCell(state.mId); cellStore = getCell(state.mId.getCellRefId());
} }
catch (...) catch (...)
{ {

View file

@ -70,7 +70,8 @@ namespace MWWorld
CellStore* getExterior(int x, int y); CellStore* getExterior(int x, int y);
CellStore* getInterior(std::string_view name); CellStore* getInterior(std::string_view name);
CellStore* getCell(std::string_view name); // interior or named exterior 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. // If cellNameInSameWorldSpace is an interior - returns this interior.
// Otherwise returns exterior cell for given position in the same world space. // 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.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) bool operator==(const CellId& left, const CellId& right)
{ {
return left.mWorldspace == right.mWorldspace && left.mPaged == right.mPaged return left.mWorldspace == right.mWorldspace && left.mPaged == right.mPaged

View file

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

View file

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

View file

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

View file

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