diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 5ebf8527c0..76b16e4cb0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 35dec69694..30a756de0b 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -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& 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().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 } + "}"; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index d94ada37e9..021cb5cc7d 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -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 destNotes; for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it) destNotes.push_back(it->second.mNote); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index c5a2083da0..93387c983a 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -56,14 +56,14 @@ namespace MWGui size_t size() const; - typedef std::multimap ContainerType; + typedef std::multimap ContainerType; typedef std::pair 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; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index f9dcbf9ce2..7798bf7105 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -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); diff --git a/apps/openmw/mwlua/types/door.cpp b/apps/openmw/mwlua/types/door.cpp index 9473ddf565..db54a77277 100644 --- a/apps/openmw/mwlua/types/door.cpp +++ b/apps/openmw/mwlua/types/door.cpp @@ -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); }; diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index 31b82b4a14..34115bf882 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -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()) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index b5a540fd81..aafb98240f 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -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); } }; diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 7b3d73057e..f1b6934ae2 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -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 followers; - getFollowers(actor, followers, mCellName.empty(), true); + + bool toExterior = MWBase::Environment::get().getWorldModel()->getCell(mCellId)->isExterior(); + getFollowers(actor, followers, toExterior, true); for (std::set::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)) diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index cbdd59cc87..377e0ce512 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -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, diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 2221ff0dec..a9af6e72a6 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -3,8 +3,11 @@ #include #include +#include #include +#include + 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); } diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index cab671bd0c..7cc60832f7 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -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 diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index cd303c1ef7..cc9f9bfe92 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -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) { diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 51aea9129a..d65b279bdf 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -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; }; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 88faac3c52..e2000785c8 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -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 { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ae830f88a4..3d0d97e272 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -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&) { diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index bd0ef53e24..16ff60db61 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -471,7 +471,7 @@ namespace MWWorld const ESM::Cell* Store::search(const ESM::RefId& cellId) const { auto foundCellIt = mCells.find(cellId); - if (foundCellIt == mCells.end()) + if (foundCellIt != mCells.end()) return &foundCellIt->second; return nullptr; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4bb56b9176..43070632eb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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 sortedDoors; for (const MWWorld::LiveCellRef& 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& 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::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 checkedCells; - std::set currentCells; - std::set nextCells; - nextCells.insert(cell->getCell()->getNameId()); + std::set checkedCells; + std::set currentCells; + std::set 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 checkedCells; - std::set currentCells; - std::set nextCells; + std::set checkedCells; + std::set currentCells; + std::set 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; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 59b308e388..e2b57c8a1a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -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; diff --git a/apps/openmw/mwworld/worldmodel.cpp b/apps/openmw/mwworld/worldmodel.cpp index ef8cad2b58..e98ce190be 100644 --- a/apps/openmw/mwworld/worldmodel.cpp +++ b/apps/openmw/mwworld/worldmodel.cpp @@ -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().search(id); + CellStore* newCellStore = nullptr; + if (!cell4) + { + const ESM::Cell* cell = mStore.get().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 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().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 (...) { diff --git a/apps/openmw/mwworld/worldmodel.hpp b/apps/openmw/mwworld/worldmodel.hpp index f2c5e0064a..04afff0095 100644 --- a/apps/openmw/mwworld/worldmodel.hpp +++ b/apps/openmw/mwworld/worldmodel.hpp @@ -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. diff --git a/components/esm3/cellid.cpp b/components/esm3/cellid.cpp index a8a812bf02..08b81fcd81 100644 --- a/components/esm3/cellid.cpp +++ b/components/esm3/cellid.cpp @@ -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 diff --git a/components/esm3/cellid.hpp b/components/esm3/cellid.hpp index 33db9e7432..65cca87bb8 100644 --- a/components/esm3/cellid.hpp +++ b/components/esm3/cellid.hpp @@ -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); diff --git a/components/esm3/custommarkerstate.cpp b/components/esm3/custommarkerstate.cpp index f752ae7c97..4568cfa748 100644 --- a/components/esm3/custommarkerstate.cpp +++ b/components/esm3/custommarkerstate.cpp @@ -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"); } diff --git a/components/esm3/custommarkerstate.hpp b/components/esm3/custommarkerstate.hpp index f4cad06edb..e1a129ffbe 100644 --- a/components/esm3/custommarkerstate.hpp +++ b/components/esm3/custommarkerstate.hpp @@ -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; diff --git a/components/esm3/loadcell.cpp b/components/esm3/loadcell.cpp index 1c4477acfd..2c6060ce8a 100644 --- a/components/esm3/loadcell.cpp +++ b/components/esm3/loadcell.cpp @@ -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; }