diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index 6885e93c19..e7b489d9ff 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -36,8 +36,6 @@ namespace MWBase virtual void newGameStarted() = 0; virtual void gameLoaded() = 0; - virtual void registerObject(const MWWorld::Ptr& ptr) = 0; - virtual void deregisterObject(const MWWorld::Ptr& ptr) = 0; virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0; virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0; virtual void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index ca84b1e9ee..46642a8d95 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -156,9 +156,6 @@ namespace MWBase virtual bool isCellQuasiExterior() const = 0; - virtual osg::Vec2f getNorthVector(const MWWorld::CellStore* cell) = 0; - ///< get north vector for given interior cell - virtual void getDoorMarkers(MWWorld::CellStore* cell, std::vector& out) = 0; ///< get a list of teleport door markers for a given cell, to be displayed on the local map @@ -263,9 +260,6 @@ namespace MWBase = 0; ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes - virtual const ESM::Cell* getExterior(const ESM::RefId& cellName) const = 0; - ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. - virtual MWWorld::Ptr getFacedObject() = 0; ///< Return pointer to the object the player is looking at, if it is within activation range diff --git a/apps/openmw/mwlua/cellbindings.cpp b/apps/openmw/mwlua/cellbindings.cpp index 9126021575..0f0062714c 100644 --- a/apps/openmw/mwlua/cellbindings.cpp +++ b/apps/openmw/mwlua/cellbindings.cpp @@ -38,8 +38,10 @@ namespace MWLua return res.str(); }; - cellT["name"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mName; }); - cellT["region"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mRegion; }); + cellT["name"] + = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mName.getRefIdString(); }); + cellT["region"] + = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mRegion.getRefIdString(); }); cellT["gridX"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridX(); }); cellT["gridY"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridY(); }); cellT["hasWater"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->hasWater(); }); @@ -57,11 +59,11 @@ namespace MWLua if constexpr (std::is_same_v) { // only for global scripts - cellT["getAll"] = [worldView = context.mWorldView, ids = getPackageToTypeTable(context.mLua->sol())]( + cellT["getAll"] = [ids = getPackageToTypeTable(context.mLua->sol())]( const CellT& cell, sol::optional type) { ObjectIdList res = std::make_shared>(); auto visitor = [&](const MWWorld::Ptr& ptr) { - worldView->getObjectRegistry()->registerPtr(ptr); + MWBase::Environment::get().getWorldModel()->registerPtr(ptr); if (getLiveCellRefType(ptr.mRef) == ptr.getType()) res->push_back(getId(ptr)); return true; diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index eb93037444..37c31abdac 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -92,14 +92,13 @@ namespace MWLua return "Unknown"; } }); - aiPackage["target"] - = sol::readonly_property([worldView = context.mWorldView](const AiPackage& p) -> sol::optional { - MWWorld::Ptr target = p.getTarget(); - if (target.isEmpty()) - return sol::nullopt; - else - return LObject(getId(target), worldView->getObjectRegistry()); - }); + aiPackage["target"] = sol::readonly_property([](const AiPackage& p) -> sol::optional { + MWWorld::Ptr target = p.getTarget(); + if (target.isEmpty()) + return sol::nullopt; + else + return LObject(getId(target)); + }); aiPackage["sideWithTarget"] = sol::readonly_property([](const AiPackage& p) { return p.sideWithTarget(); }); aiPackage["destPosition"] = sol::readonly_property([](const AiPackage& p) { return p.getDestination(); }); diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 452caea9b8..eb897e820d 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -78,20 +78,11 @@ namespace MWLua sol::table api(context.mLua->sol(), sol::create); WorldView* worldView = context.mWorldView; addTimeBindings(api, context, true); - api["getCellByName"] = [worldView = context.mWorldView](const std::string& name) -> sol::optional { - MWWorld::CellStore* cell = worldView->findNamedCell(ESM::RefId::stringRefId(name)); - if (cell) - return GCell{ cell }; - else - return sol::nullopt; - }; - api["getExteriorCell"] = [worldView = context.mWorldView](int x, int y) -> sol::optional { - MWWorld::CellStore* cell = worldView->findExteriorCell(x, y); - if (cell) - return GCell{ cell }; - else - return sol::nullopt; + api["getCellByName"] = [](std::string_view name) { + return GCell{ MWBase::Environment::get().getWorldModel()->getCell(ESM::RefId::stringRefId(name)) }; }; + api["getExteriorCell"] + = [](int x, int y) { return GCell{ MWBase::Environment::get().getWorldModel()->getExterior(x, y) }; }; api["activeActors"] = GObjectList{ worldView->getActorsInScene() }; // TODO: add world.placeNewObject(recordId, cell, pos, [rot]) return LuaUtil::makeReadOnly(api); diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 103a14e9bb..c11a87eaac 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -55,10 +55,10 @@ namespace MWLua Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion(); mLua.addInternalLibSearchPath(libsDir); - mGlobalSerializer = createUserdataSerializer(false, mWorldView.getObjectRegistry()); - mLocalSerializer = createUserdataSerializer(true, mWorldView.getObjectRegistry()); - mGlobalLoader = createUserdataSerializer(false, mWorldView.getObjectRegistry(), &mContentFileMapping); - mLocalLoader = createUserdataSerializer(true, mWorldView.getObjectRegistry(), &mContentFileMapping); + mGlobalSerializer = createUserdataSerializer(false); + mLocalSerializer = createUserdataSerializer(true); + mGlobalLoader = createUserdataSerializer(false, &mContentFileMapping); + mLocalLoader = createUserdataSerializer(true, &mContentFileMapping); mGlobalScripts.setSerializer(mGlobalSerializer.get()); } @@ -141,7 +141,6 @@ namespace MWLua return; // The game is not started yet. float frameDuration = MWBase::Environment::get().getFrameDuration(); - ObjectRegistry* objectRegistry = mWorldView.getObjectRegistry(); MWWorld::Ptr newPlayerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (!(getId(mPlayer) == getId(newPlayerPtr))) @@ -149,7 +148,7 @@ namespace MWLua if (!mPlayer.isInCell() || !newPlayerPtr.isInCell() || mPlayer.getCell() != newPlayerPtr.getCell()) { mPlayer = newPlayerPtr; // player was moved to another cell, update ptr in registry - objectRegistry->registerPtr(mPlayer); + MWBase::Environment::get().getWorldModel()->registerPtr(mPlayer); } mWorldView.update(); @@ -179,7 +178,7 @@ namespace MWLua mGlobalScripts.receiveEvent(e.mEventName, e.mEventData); for (LocalEvent& e : localEvents) { - LObject obj(e.mDest, objectRegistry); + LObject obj(e.mDest); LocalScripts* scripts = obj.isValid() ? obj.ptr().getRefData().getLuaScripts() : nullptr; if (scripts) scripts->receiveEvent(e.mEventName, e.mEventData); @@ -196,7 +195,7 @@ namespace MWLua // Engine handlers in local scripts for (const LocalEngineEvent& e : mLocalEngineEvents) { - LObject obj(e.mDest, objectRegistry); + LObject obj(e.mDest); if (!obj.isValid()) { if (luaDebug) @@ -220,7 +219,7 @@ namespace MWLua if (mPlayerChanged) { mPlayerChanged = false; - mGlobalScripts.playerAdded(GObject(getId(mPlayer), objectRegistry)); + mGlobalScripts.playerAdded(GObject(getId(mPlayer))); } if (mNewGameStarted) { @@ -230,7 +229,7 @@ namespace MWLua for (ObjectId id : mObjectAddedEvents) { - GObject obj(id, objectRegistry); + GObject obj(id); if (obj.isValid()) { mGlobalScripts.objectActive(obj); @@ -277,11 +276,11 @@ namespace MWLua mInGameConsoleMessages.clear(); for (std::unique_ptr& action : mActionQueue) - action->safeApply(mWorldView); + action->safeApply(); mActionQueue.clear(); if (mTeleportPlayerAction) - mTeleportPlayerAction->safeApply(mWorldView); + mTeleportPlayerAction->safeApply(); mTeleportPlayerAction.reset(); } @@ -378,32 +377,20 @@ namespace MWLua if (localScripts) { mActiveLocalScripts.erase(localScripts); - if (!mWorldView.getObjectRegistry()->getPtr(getId(ptr), true).isEmpty()) + if (!MWBase::Environment::get().getWorldModel()->getPtr(getId(ptr)).isEmpty()) mLocalEngineEvents.push_back({ getId(ptr), LocalScripts::OnInactive{} }); } } - void LuaManager::registerObject(const MWWorld::Ptr& ptr) - { - mWorldView.getObjectRegistry()->registerPtr(ptr); - } - - void LuaManager::deregisterObject(const MWWorld::Ptr& ptr) - { - mWorldView.getObjectRegistry()->deregisterPtr(ptr); - } - void LuaManager::itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) { - mWorldView.getObjectRegistry()->registerPtr(consumable); - mLocalEngineEvents.push_back( - { getId(actor), LocalScripts::OnConsume{ LObject(getId(consumable), mWorldView.getObjectRegistry()) } }); + MWBase::Environment::get().getWorldModel()->registerPtr(consumable); + mLocalEngineEvents.push_back({ getId(actor), LocalScripts::OnConsume{ LObject(getId(consumable)) } }); } void LuaManager::objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) { - mLocalEngineEvents.push_back( - { getId(object), LocalScripts::OnActivated{ LObject(getId(actor), mWorldView.getObjectRegistry()) } }); + mLocalEngineEvents.push_back({ getId(object), LocalScripts::OnActivated{ LObject(getId(actor)) } }); } MWBase::LuaManager::ActorControls* LuaManager::getActorControls(const MWWorld::Ptr& ptr) const @@ -437,7 +424,7 @@ namespace MWLua throw std::runtime_error("Lua scripts on static objects are not allowed"); else if (type == ESM::REC_INTERNAL_PLAYER) { - scripts = std::make_shared(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry())); + scripts = std::make_shared(&mLua, LObject(getId(ptr))); scripts->setAutoStartConf(mConfiguration.getPlayerConf()); scripts->addPackage("openmw.ui", mUserInterfacePackage); scripts->addPackage("openmw.camera", mCameraPackage); @@ -448,7 +435,7 @@ namespace MWLua } else { - scripts = std::make_shared(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry())); + scripts = std::make_shared(&mLua, LObject(getId(ptr))); if (!autoStartConf.has_value()) autoStartConf = mConfiguration.getLocalConf(type, ptr.getCellRef().getRefId(), getId(ptr)); scripts->setAutoStartConf(std::move(*autoStartConf)); @@ -507,7 +494,7 @@ namespace MWLua return; } - mWorldView.getObjectRegistry()->registerPtr(ptr); + MWBase::Environment::get().getWorldModel()->registerPtr(ptr); LocalScripts* scripts = createLocalScripts(ptr); scripts->setSerializer(mLocalSerializer.get()); @@ -515,7 +502,7 @@ namespace MWLua scripts->load(data); // LiveCellRef is usually copied after loading, so this Ptr will become invalid and should be deregistered. - mWorldView.getObjectRegistry()->deregisterPtr(ptr); + MWBase::Environment::get().getWorldModel()->deregisterPtr(ptr); } void LuaManager::reloadAllScripts() @@ -536,7 +523,7 @@ namespace MWLua mGlobalScripts.load(data); } - for (const auto& [id, ptr] : mWorldView.getObjectRegistry()->mObjectMapping) + for (const auto& [id, ptr] : MWBase::Environment::get().getWorldModel()->getAllPtrs()) { // Reload local scripts LocalScripts* scripts = ptr.getRefData().getLuaScripts(); if (scripts == nullptr) @@ -564,7 +551,7 @@ namespace MWLua } sol::object selected = sol::nil; if (!selectedPtr.isEmpty()) - selected = sol::make_object(mLua.sol(), LObject(getId(selectedPtr), mWorldView.getObjectRegistry())); + selected = sol::make_object(mLua.sol(), LObject(getId(selectedPtr))); if (!playerScripts->consoleCommand(consoleMode, command, selected)) MWBase::Environment::get().getWindowManager()->printToConsole( "No Lua handlers for console\n", MWBase::WindowManager::sConsoleColor_Error); @@ -577,11 +564,11 @@ namespace MWLua mCallerTraceback = state->debugTraceback(); } - void LuaManager::Action::safeApply(WorldView& w) const + void LuaManager::Action::safeApply() const { try { - apply(w); + apply(); } catch (const std::exception& e) { @@ -606,7 +593,7 @@ namespace MWLua { } - void apply(WorldView&) const override { mFn(); } + void apply() const override { mFn(); } std::string toString() const override { return "FunctionAction " + mName; } private: diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index a4037593da..9d02d11a3d 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -47,8 +47,6 @@ namespace MWLua void gameLoaded() override; void objectAddedToScene(const MWWorld::Ptr& ptr) override; void objectRemovedFromScene(const MWWorld::Ptr& ptr) override; - void registerObject(const MWWorld::Ptr& ptr) override; - void deregisterObject(const MWWorld::Ptr& ptr) override; void inputEvent(const InputEvent& event) override { mInputEvents.push_back(event); } void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) override; void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) override; @@ -75,8 +73,8 @@ namespace MWLua Action(LuaUtil::LuaState* state); virtual ~Action() {} - void safeApply(WorldView&) const; - virtual void apply(WorldView&) const = 0; + void safeApply() const; + virtual void apply() const = 0; virtual std::string toString() const = 0; private: diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 10507e3746..f48c57b6db 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -45,11 +45,11 @@ namespace MWLua return sol::nullopt; }); rayResult["hitObject"] - = sol::readonly_property([worldView](const MWPhysics::RayCastingResult& r) -> sol::optional { + = sol::readonly_property([](const MWPhysics::RayCastingResult& r) -> sol::optional { if (r.mHitObject.isEmpty()) return sol::nullopt; else - return LObject(getId(r.mHitObject), worldView->getObjectRegistry()); + return LObject(getId(r.mHitObject)); }); api["COLLISION_TYPE"] diff --git a/apps/openmw/mwlua/object.cpp b/apps/openmw/mwlua/object.cpp index 0a069f35c7..446bf4443f 100644 --- a/apps/openmw/mwlua/object.cpp +++ b/apps/openmw/mwlua/object.cpp @@ -39,10 +39,11 @@ namespace MWLua bool Object::isValid() const { - if (mLastUpdate < mObjectRegistry->mUpdateCounter) + MWWorld::WorldModel& w = *MWBase::Environment::get().getWorldModel(); + if (mLastUpdate < w.getPtrIndexUpdateCounter()) { - updatePtr(); - mLastUpdate = mObjectRegistry->mUpdateCounter; + mPtr = w.getPtr(mId); + mLastUpdate = w.getPtrIndexUpdateCounter(); } return !mPtr.isEmpty(); } @@ -53,56 +54,4 @@ namespace MWLua throw std::runtime_error("Object is not available: " + idToString(mId)); return mPtr; } - - void ObjectRegistry::update() - { - if (mChanged) - { - mUpdateCounter++; - mChanged = false; - } - } - - void ObjectRegistry::clear() - { - mObjectMapping.clear(); - mChanged = false; - mUpdateCounter = 0; - mLastAssignedId.unset(); - } - - MWWorld::Ptr ObjectRegistry::getPtr(ObjectId id, bool local) - { - MWWorld::Ptr ptr; - auto it = mObjectMapping.find(id); - if (it != mObjectMapping.end()) - ptr = it->second; - if (local) - { - // TODO: Return ptr only if it is active or was active in the previous frame, otherwise return empty. - // Needed because in multiplayer inactive objects will not be synchronized, so an be out of date. - } - else - { - // TODO: If Ptr is empty then try to load the object from esp/esm3. - } - return ptr; - } - - ObjectId ObjectRegistry::registerPtr(const MWWorld::Ptr& ptr) - { - ObjectId id = ptr.getCellRef().getOrAssignRefNum(mLastAssignedId); - mChanged = true; - mObjectMapping[id] = ptr; - return id; - } - - ObjectId ObjectRegistry::deregisterPtr(const MWWorld::Ptr& ptr) - { - ObjectId id = getId(ptr); - mChanged = true; - mObjectMapping.erase(id); - return id; - } - } diff --git a/apps/openmw/mwlua/object.hpp b/apps/openmw/mwlua/object.hpp index cf715256a7..9c720c67ce 100644 --- a/apps/openmw/mwlua/object.hpp +++ b/apps/openmw/mwlua/object.hpp @@ -8,7 +8,9 @@ #include +#include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" +#include "../mwworld/worldmodel.hpp" namespace MWLua { @@ -23,46 +25,14 @@ namespace MWLua std::string ptrToString(const MWWorld::Ptr& ptr); bool isMarker(const MWWorld::Ptr& ptr); - // Holds a mapping ObjectId -> MWWord::Ptr. - class ObjectRegistry - { - public: - ObjectRegistry() { mLastAssignedId.unset(); } - - void update(); // Should be called every frame. - void clear(); // Should be called before starting or loading a new game. - - ObjectId registerPtr(const MWWorld::Ptr& ptr); - ObjectId deregisterPtr(const MWWorld::Ptr& ptr); - - // Returns Ptr by id. If object is not found, returns empty Ptr. - // If local = true, returns non-empty ptr only if it can be used in local scripts - // (i.e. is active or was active in the previous frame). - MWWorld::Ptr getPtr(ObjectId id, bool local); - - // Needed only for saving/loading. - const ObjectId& getLastAssignedId() const { return mLastAssignedId; } - void setLastAssignedId(ObjectId id) { mLastAssignedId = id; } - - private: - friend class Object; - friend class LuaManager; - - bool mChanged = false; - int64_t mUpdateCounter = 0; - std::map mObjectMapping; - ObjectId mLastAssignedId; - }; - // Lua scripts can't use MWWorld::Ptr directly, because lifetime of a script can be longer than lifetime of Ptr. // `GObject` and `LObject` are intended to be passed to Lua as a userdata. // It automatically updates the underlying Ptr when needed. class Object { public: - Object(ObjectId id, ObjectRegistry* reg) + Object(ObjectId id) : mId(id) - , mObjectRegistry(reg) { } virtual ~Object() {} @@ -80,13 +50,10 @@ namespace MWLua virtual sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const = 0; // returns LCell or GCell protected: - virtual void updatePtr() const = 0; - const ObjectId mId; - ObjectRegistry* mObjectRegistry; mutable MWWorld::Ptr mPtr; - mutable int64_t mLastUpdate = -1; + mutable size_t mLastUpdate = 0; }; // Used only in local scripts @@ -97,11 +64,7 @@ namespace MWLua class LObject : public Object { using Object::Object; - void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, true); } - sol::object getObject(lua_State* lua, ObjectId id) const final - { - return sol::make_object(lua, id, mObjectRegistry); - } + sol::object getObject(lua_State* lua, ObjectId id) const final { return sol::make_object(lua, id); } sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const final { return sol::make_object(lua, LCell{ store }); @@ -116,11 +79,7 @@ namespace MWLua class GObject : public Object { using Object::Object; - void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, false); } - sol::object getObject(lua_State* lua, ObjectId id) const final - { - return sol::make_object(lua, id, mObjectRegistry); - } + sol::object getObject(lua_State* lua, ObjectId id) const final { return sol::make_object(lua, id); } sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const final { return sol::make_object(lua, GCell{ store }); diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index ba6c53e3d4..ab0e686e86 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -61,14 +61,12 @@ namespace MWLua { } - void apply(WorldView& worldView) const override + void apply() const override { - MWWorld::CellStore* cell = worldView.findCell(mCell, mPos); - if (!cell) - throw std::runtime_error(std::string("cell not found: '") + mCell.getRefIdString() + "'"); - + MWWorld::WorldModel& wm = *MWBase::Environment::get().getWorldModel(); + MWWorld::CellStore* cell = wm.getCellByPosition(mPos, mCell); MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false); + MWWorld::Ptr obj = wm.getPtr(mObject); const MWWorld::Class& cls = obj.getClass(); bool isPlayer = obj == world->getPlayerPtr(); if (cls.isActor()) @@ -111,12 +109,12 @@ namespace MWLua { } - void apply(WorldView& worldView) const override + void apply() const override { - MWWorld::Ptr object = worldView.getObjectRegistry()->getPtr(mObject, true); + MWWorld::Ptr object = MWBase::Environment::get().getWorldModel()->getPtr(mObject); if (object.isEmpty()) throw std::runtime_error(std::string("Object not found: " + idToString(mObject))); - MWWorld::Ptr actor = worldView.getObjectRegistry()->getPtr(mActor, true); + MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor); if (actor.isEmpty()) throw std::runtime_error(std::string("Actor not found: " + idToString(mActor))); @@ -147,14 +145,13 @@ namespace MWLua { using ListT = ObjectList; sol::state_view& lua = context.mLua->sol(); - ObjectRegistry* registry = context.mWorldView->getObjectRegistry(); sol::usertype listT = lua.new_usertype(prefix + "ObjectList"); listT[sol::meta_function::to_string] = [](const ListT& list) { return "{" + std::to_string(list.mIds->size()) + " objects}"; }; listT[sol::meta_function::length] = [](const ListT& list) { return list.mIds->size(); }; - listT[sol::meta_function::index] = [registry](const ListT& list, size_t index) { + listT[sol::meta_function::index] = [](const ListT& list, size_t index) { if (index > 0 && index <= list.mIds->size()) - return ObjectT((*list.mIds)[index - 1], registry); + return ObjectT((*list.mIds)[index - 1]); else throw std::runtime_error("Index out of range"); }; @@ -167,7 +164,7 @@ namespace MWLua { objectT["isValid"] = [](const ObjectT& o) { return o.isValid(); }; objectT["recordId"] = sol::readonly_property( - [](const ObjectT& o) -> ESM::RefId { return o.ptr().getCellRef().getRefId(); }); + [](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().getRefIdString(); }); objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional> { const MWWorld::Ptr& ptr = o.ptr(); if (ptr.isInCell()) @@ -265,7 +262,7 @@ namespace MWLua inventoryT[sol::meta_function::to_string] = [](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; }; - inventoryT["getAll"] = [worldView = context.mWorldView, ids = getPackageToTypeTable(context.mLua->sol())]( + inventoryT["getAll"] = [ids = getPackageToTypeTable(context.mLua->sol())]( const InventoryT& inventory, sol::optional type) { int mask = -1; sol::optional typeId = sol::nullopt; @@ -329,16 +326,16 @@ namespace MWLua while (it.getType() != -1) { const MWWorld::Ptr& item = *(it++); - worldView->getObjectRegistry()->registerPtr(item); + MWBase::Environment::get().getWorldModel()->registerPtr(item); list->push_back(getId(item)); } return ObjectList{ list }; }; - inventoryT["countOf"] = [](const InventoryT& inventory, const ESM::RefId& recordId) { + inventoryT["countOf"] = [](const InventoryT& inventory, const std::string& recordId) { const MWWorld::Ptr& ptr = inventory.mObj.ptr(); MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); - return store.count(recordId); + return store.count(ESM::RefId::stringRefId(recordId)); }; if constexpr (std::is_same_v) diff --git a/apps/openmw/mwlua/postprocessingbindings.cpp b/apps/openmw/mwlua/postprocessingbindings.cpp index 96e4ac27d3..4d8212b749 100644 --- a/apps/openmw/mwlua/postprocessingbindings.cpp +++ b/apps/openmw/mwlua/postprocessingbindings.cpp @@ -20,7 +20,7 @@ namespace { } - void apply(MWLua::WorldView&) const override + void apply() const override { MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(mShader, mName, mValue); } diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 71e0aa8889..8740ae15ee 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -97,9 +97,9 @@ namespace MWLua { } - void apply(WorldView& worldView) const override + void apply() const override { - LObject obj(mId, worldView.getObjectRegistry()); + LObject obj(mId); LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); if (scripts) scripts->applyStatsCache(); diff --git a/apps/openmw/mwlua/types/actor.cpp b/apps/openmw/mwlua/types/actor.cpp index 395596dd3d..518863371d 100644 --- a/apps/openmw/mwlua/types/actor.cpp +++ b/apps/openmw/mwlua/types/actor.cpp @@ -29,20 +29,20 @@ namespace MWLua { } - void apply(WorldView& worldView) const override + void apply() const override { - MWWorld::Ptr actor = worldView.getObjectRegistry()->getPtr(mActor, false); + MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor); MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); std::array usedSlots; std::fill(usedSlots.begin(), usedSlots.end(), false); static constexpr int anySlot = -1; - auto tryEquipToSlot = [&actor, &store, &usedSlots, &worldView](int slot, const Item& item) -> bool { + auto tryEquipToSlot = [&actor, &store, &usedSlots](int slot, const Item& item) -> bool { auto old_it = slot != anySlot ? store.getSlot(slot) : store.end(); MWWorld::Ptr itemPtr; if (std::holds_alternative(item)) { - itemPtr = worldView.getObjectRegistry()->getPtr(std::get(item), false); + itemPtr = MWBase::Environment::get().getWorldModel()->getPtr(std::get(item)); if (old_it != store.end() && *old_it == itemPtr) return true; // already equipped if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0 @@ -187,7 +187,7 @@ namespace MWLua auto it = store.getSlot(slot); if (it == store.end()) continue; - context.mWorldView->getObjectRegistry()->registerPtr(*it); + MWBase::Environment::get().getWorldModel()->registerPtr(*it); equipment[slot] = o.getObject(context.mLua->sol(), getId(*it)); } return equipment; @@ -201,7 +201,7 @@ namespace MWLua auto it = store.getSlot(slot); if (it == store.end()) return sol::nil; - context.mWorldView->getObjectRegistry()->registerPtr(*it); + MWBase::Environment::get().getWorldModel()->registerPtr(*it); return o.getObject(context.mLua->sol(), getId(*it)); }; actor["equipment"] = sol::overload(getAllEquipment, getEquipmentFromSlot); diff --git a/apps/openmw/mwlua/types/door.cpp b/apps/openmw/mwlua/types/door.cpp index 924b0ef1db..d44c604822 100644 --- a/apps/openmw/mwlua/types/door.cpp +++ b/apps/openmw/mwlua/types/door.cpp @@ -32,15 +32,14 @@ namespace MWLua = [](const Object& o) -> osg::Vec3f { return doorPtr(o).getCellRef().getDoorDest().asVec3(); }; door["destRotation"] = [](const Object& o) -> osg::Vec3f { return doorPtr(o).getCellRef().getDoorDest().asRotationVec3(); }; - door["destCell"] = [worldView = context.mWorldView](sol::this_state lua, const Object& o) -> sol::object { + door["destCell"] = [](sol::this_state lua, const Object& o) -> sol::object { const MWWorld::CellRef& cellRef = doorPtr(o).getCellRef(); if (!cellRef.getTeleport()) return sol::nil; - MWWorld::CellStore* cell = worldView->findCell(cellRef.getDestCell(), cellRef.getDoorDest().asVec3()); - if (cell) - return o.getCell(lua, cell); - else - return sol::nil; + MWWorld::CellStore* cell = MWBase::Environment::get().getWorldModel()->getCellByPosition( + cellRef.getDoorDest().asVec3(), cellRef.getDestCell()); + assert(cell); + return o.getCell(lua, cell); }; auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index fdc58554fe..ccce8b5b35 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -35,7 +35,7 @@ namespace MWLua { } - void apply(WorldView&) const override + void apply() const override { try { diff --git a/apps/openmw/mwlua/userdataserializer.cpp b/apps/openmw/mwlua/userdataserializer.cpp index 38d29a42ef..b7ee0a2eda 100644 --- a/apps/openmw/mwlua/userdataserializer.cpp +++ b/apps/openmw/mwlua/userdataserializer.cpp @@ -10,9 +10,8 @@ namespace MWLua class Serializer final : public LuaUtil::UserdataSerializer { public: - explicit Serializer(bool localSerializer, ObjectRegistry* registry, std::map* contentFileMapping) + explicit Serializer(bool localSerializer, std::map* contentFileMapping) : mLocalSerializer(localSerializer) - , mObjectRegistry(registry) , mContentFileMapping(contentFileMapping) { } @@ -44,23 +43,22 @@ namespace MWLua id.mContentFile = iter->second; } if (mLocalSerializer) - sol::stack::push(lua, LObject(id, mObjectRegistry)); + sol::stack::push(lua, LObject(id)); else - sol::stack::push(lua, GObject(id, mObjectRegistry)); + sol::stack::push(lua, GObject(id)); return true; } return false; } bool mLocalSerializer; - ObjectRegistry* mObjectRegistry; std::map* mContentFileMapping; }; std::unique_ptr createUserdataSerializer( - bool local, ObjectRegistry* registry, std::map* contentFileMapping) + bool local, std::map* contentFileMapping) { - return std::make_unique(local, registry, contentFileMapping); + return std::make_unique(local, contentFileMapping); } } diff --git a/apps/openmw/mwlua/userdataserializer.hpp b/apps/openmw/mwlua/userdataserializer.hpp index 70af7ebd06..f52bb22723 100644 --- a/apps/openmw/mwlua/userdataserializer.hpp +++ b/apps/openmw/mwlua/userdataserializer.hpp @@ -16,7 +16,7 @@ namespace MWLua // contentFileMapping is used only for deserialization. Needed to fix references if the order // of content files was changed. std::unique_ptr createUserdataSerializer( - bool local, ObjectRegistry* registry, std::map* contentFileMapping = nullptr); + bool local, std::map* contentFileMapping = nullptr); } #endif // MWLUA_USERDATASERIALIZER_H diff --git a/apps/openmw/mwlua/worldview.cpp b/apps/openmw/mwlua/worldview.cpp index 16dbbb7359..cc1d381531 100644 --- a/apps/openmw/mwlua/worldview.cpp +++ b/apps/openmw/mwlua/worldview.cpp @@ -18,7 +18,6 @@ namespace MWLua void WorldView::update() { - mObjectRegistry.update(); mActivatorsInScene.updateList(); mActorsInScene.updateList(); mContainersInScene.updateList(); @@ -29,7 +28,6 @@ namespace MWLua void WorldView::clear() { - mObjectRegistry.clear(); mActivatorsInScene.clear(); mActorsInScene.clear(); mContainersInScene.clear(); @@ -59,7 +57,7 @@ namespace MWLua void WorldView::objectAddedToScene(const MWWorld::Ptr& ptr) { - mObjectRegistry.registerPtr(ptr); + MWBase::Environment::get().getWorldModel()->registerPtr(ptr); ObjectGroup* group = chooseGroup(ptr); if (group) addToGroup(*group, ptr); @@ -84,13 +82,13 @@ namespace MWLua esm.getHNT(mSimulationTime, "LUAW"); ObjectId lastAssignedId; lastAssignedId.load(esm, true); - mObjectRegistry.setLastAssignedId(lastAssignedId); + MWBase::Environment::get().getWorldModel()->setLastGeneratedRefNum(lastAssignedId); } void WorldView::save(ESM::ESMWriter& esm) const { esm.writeHNT("LUAW", mSimulationTime); - mObjectRegistry.getLastAssignedId().save(esm, true); + MWBase::Environment::get().getWorldModel()->getLastGeneratedRefNum().save(esm, true); } void WorldView::ObjectGroup::updateList() @@ -122,35 +120,4 @@ namespace MWLua group.mSet.erase(getId(ptr)); group.mChanged = true; } - - // TODO: If Lua scripts will use several threads at the same time, then `find*Cell` functions should have critical - // sections. - MWWorld::CellStore* WorldView::findCell(const ESM::RefId& name, osg::Vec3f position) - { - MWWorld::WorldModel* worldModel = MWBase::Environment::get().getWorldModel(); - bool exterior = name.empty() || MWBase::Environment::get().getWorld()->getExterior(name); - if (exterior) - { - const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(position.x(), position.y()); - return worldModel->getExterior(cellIndex.x(), cellIndex.y()); - } - else - return worldModel->getInterior(name); - } - - MWWorld::CellStore* WorldView::findNamedCell(const ESM::RefId& name) - { - MWWorld::WorldModel* worldModel = MWBase::Environment::get().getWorldModel(); - const ESM::Cell* esmCell = MWBase::Environment::get().getWorld()->getExterior(name); - if (esmCell) - return worldModel->getExterior(esmCell->getGridX(), esmCell->getGridY()); - else - return worldModel->getInterior(name); - } - - MWWorld::CellStore* WorldView::findExteriorCell(int x, int y) - { - return MWBase::Environment::get().getWorldModel()->getExterior(x, y); - } - } diff --git a/apps/openmw/mwlua/worldview.hpp b/apps/openmw/mwlua/worldview.hpp index 56136d3ee7..b0c09ed475 100644 --- a/apps/openmw/mwlua/worldview.hpp +++ b/apps/openmw/mwlua/worldview.hpp @@ -17,7 +17,11 @@ namespace ESM namespace MWLua { - // Tracks all used game objects. + // WorldView is a kind of an extension to mwworld. It was created on initial stage of + // OpenMW Lua development in order to minimize the risk of merge conflicts. + // TODO: Move get*InScene functions to mwworld/scene + // TODO: Move time-related stuff to mwworld; maybe create a new class TimeManager. + // TODO: Remove WorldView. class WorldView { public: @@ -43,21 +47,9 @@ namespace MWLua ObjectIdList getDoorsInScene() const { return mDoorsInScene.mList; } ObjectIdList getItemsInScene() const { return mItemsInScene.mList; } - ObjectRegistry* getObjectRegistry() { return &mObjectRegistry; } - - void objectUnloaded(const MWWorld::Ptr& ptr) { mObjectRegistry.deregisterPtr(ptr); } - void objectAddedToScene(const MWWorld::Ptr& ptr); void objectRemovedFromScene(const MWWorld::Ptr& ptr); - // Returns list of objects that meets the `query` criteria. - // If onlyActive = true, then search only among the objects that are currently in the scene. - // TODO: ObjectIdList selectObjects(const Queries::Query& query, bool onlyActive); - - MWWorld::CellStore* findCell(const ESM::RefId& name, osg::Vec3f position); - MWWorld::CellStore* findNamedCell(const ESM::RefId& name); - MWWorld::CellStore* findExteriorCell(int x, int y); - void load(ESM::ESMReader& esm); void save(ESM::ESMWriter& esm) const; @@ -79,7 +71,6 @@ namespace MWLua void addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr); void removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr); - ObjectRegistry mObjectRegistry; ObjectGroup mActivatorsInScene; ObjectGroup mActorsInScene; ObjectGroup mContainersInScene; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 1da98c483c..f9c7e02e44 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -281,6 +281,19 @@ namespace MWRender } } + static osg::Vec2f getNorthVector(const MWWorld::CellStore* cell) + { + MWWorld::ConstPtr northmarker = cell->searchConst(ESM::RefId::stringRefId("northmarker")); + + if (northmarker.isEmpty()) + return osg::Vec2f(0, 1); + + osg::Quat orient(-northmarker.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, 1)); + osg::Vec3f dir = orient * osg::Vec3f(0, 1, 0); + osg::Vec2f d(dir.x(), dir.y()); + return d; + } + void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell) { osg::ComputeBoundsVisitor computeBoundsVisitor; @@ -299,7 +312,7 @@ namespace MWRender mBounds = bounds; // Get the cell's NorthMarker rotation. This is used to rotate the entire map. - osg::Vec2f north = MWBase::Environment::get().getWorld()->getNorthVector(cell); + osg::Vec2f north = getNorthVector(cell); mAngle = std::atan2(north.x(), north.y()); diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 348ff2340b..f5ba056338 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -399,24 +399,20 @@ namespace MWScript MWWorld::CellStore* store = nullptr; try { - store = worldModel->getInterior(cellID); + store = worldModel->getCell(cellID); } catch (std::exception&) { // cell not found, move to exterior instead if moving the player (vanilla PositionCell // compatibility) - const ESM::Cell* cell = world->getExterior(cellID); - if (!cell) - { - std::string error - = "Warning: PositionCell: unknown interior cell (" + cellID.getRefIdString() + ")"; - if (isPlayer) - error += ", moving to exterior instead"; - runtime.getContext().report(error); - Log(Debug::Warning) << error; - if (!isPlayer) - return; - } + std::string error + = "Warning: PositionCell: unknown interior cell (" + cellID.getRefIdString() + ")"; + if (isPlayer) + error += ", moving to exterior instead"; + runtime.getContext().report(error); + Log(Debug::Warning) << error; + if (!isPlayer) + return; const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y); store = worldModel->getExterior(cellIndex.x(), cellIndex.y()); } @@ -519,18 +515,12 @@ namespace MWScript MWWorld::CellStore* store = nullptr; try { - store = MWBase::Environment::get().getWorldModel()->getInterior(cellID); + store = MWBase::Environment::get().getWorldModel()->getCell(cellID); } catch (std::exception&) { - const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); - const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y); - store = MWBase::Environment::get().getWorldModel()->getExterior(cellIndex.x(), cellIndex.y()); - if (!cell) - { - runtime.getContext().report("unknown cell (" + cellID.getRefIdString() + ")"); - Log(Debug::Error) << "Error: unknown cell (" << cellID << ")"; - } + runtime.getContext().report("unknown cell (" + cellID.getRefIdString() + ")"); + Log(Debug::Error) << "Error: unknown cell (" << cellID << ")"; } if (store) { diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 2f4e4107d8..2fddf72975 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -53,6 +53,7 @@ #include "containerstore.hpp" #include "esmstore.hpp" #include "ptr.hpp" +#include "worldmodel.hpp" namespace { @@ -266,7 +267,7 @@ namespace MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore)); } else - MWBase::Environment::get().getLuaManager()->registerObject(MWWorld::Ptr(&*iter, cellstore)); + MWBase::Environment::get().getWorldModel()->registerPtr(MWWorld::Ptr(&*iter, cellstore)); return; } @@ -281,7 +282,7 @@ namespace collection.mList.push_back(ref); MWWorld::LiveCellRefBase* base = &collection.mList.back(); - MWBase::Environment::get().getLuaManager()->registerObject(MWWorld::Ptr(base, cellstore)); + MWBase::Environment::get().getWorldModel()->registerPtr(MWWorld::Ptr(base, cellstore)); } // this function allows us to link a CellRefList to the associated recNameInt, and apply a function @@ -416,7 +417,7 @@ namespace MWWorld if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty()) throw std::runtime_error("moveTo: object is not in this cell"); - MWBase::Environment::get().getLuaManager()->registerObject(MWWorld::Ptr(object.getBase(), cellToMoveTo)); + MWBase::Environment::get().getWorldModel()->registerPtr(MWWorld::Ptr(object.getBase(), cellToMoveTo)); MovedRefTracker::iterator found = mMovedHere.find(object.getBase()); if (found != mMovedHere.end()) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 328610bc40..e666821579 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -317,6 +317,7 @@ namespace MWWorld mLocalScripts.clear(); mWorldScene->clear(); + mWorldModel.clear(); mStore.clearDynamic(); @@ -328,8 +329,6 @@ namespace MWWorld mPlayer->set(mStore.get().find(ESM::RefId::stringRefId("Player"))); } - mWorldModel.clear(); - mDoorStates.clear(); mGoToJail = false; @@ -565,34 +564,6 @@ namespace MWWorld mRandomSeed = seed; } - const ESM::Cell* World::getExterior(const ESM::RefId& cellName) const - { - // first try named cells - const ESM::Cell* cell = mStore.get().searchExtByName(cellName); - if (cell) - return cell; - // treat "Wilderness" like an empty string - static const ESM::RefId defaultName - = ESM::RefId::stringRefId(mStore.get().find("sDefaultCellname")->mValue.getString()); - if (cellName == defaultName) - { - cell = mStore.get().searchExtByName(ESM::RefId::sEmpty); - if (cell) - return cell; - } - - // didn't work -> now check for regions - for (const ESM::Region& region : mStore.get()) - { - if (cellName == ESM::RefId::stringRefId(region.mName)) - { - return mStore.get().searchExtByRegion(region.mId); - } - } - - return nullptr; - } - void World::useDeathCamera() { mRendering->getCamera()->setMode(MWRender::Camera::Mode::ThirdPerson); @@ -816,7 +787,7 @@ namespace MWWorld void World::enable(const Ptr& reference) { - MWBase::Environment::get().getLuaManager()->registerObject(reference); + MWBase::Environment::get().getWorldModel()->registerPtr(reference); if (!reference.isInCell()) return; @@ -868,7 +839,7 @@ namespace MWWorld if (reference == getPlayerPtr()) throw std::runtime_error("can not disable player object"); - MWBase::Environment::get().getLuaManager()->deregisterObject(reference); + MWBase::Environment::get().getWorldModel()->deregisterPtr(reference); reference.getRefData().disable(); if (reference.getCellRef().getRefNum().hasContentFile()) @@ -2051,19 +2022,6 @@ namespace MWWorld mWeatherManager->modRegion(regionid, chances); } - osg::Vec2f World::getNorthVector(const CellStore* cell) - { - MWWorld::ConstPtr northmarker = cell->searchConst(ESM::RefId::stringRefId("northmarker")); - - if (northmarker.isEmpty()) - return osg::Vec2f(0, 1); - - osg::Quat orient(-northmarker.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, 1)); - osg::Vec3f dir = orient * osg::Vec3f(0, 1, 0); - osg::Vec2f d(dir.x(), dir.y()); - return d; - } - struct GetDoorMarkerVisitor { std::vector& mOut; @@ -2860,7 +2818,17 @@ namespace MWWorld { pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; const std::string& name = nameId.getRefIdString(); - const ESM::Cell* ext = getExterior(nameId); + + const ESM::Cell* ext = nullptr; + try + { + ext = mWorldModel.getCell(nameId)->getCell(); + if (!ext->isExterior()) + return false; + } + catch (std::exception&) + { + } if (!ext) { size_t comma = name.find(','); @@ -3402,7 +3370,7 @@ namespace MWWorld void World::rest(double hours) { - mWorldModel.rest(hours); + mWorldModel.forEachLoadedCellStore([hours](CellStore& store) { store.rest(hours); }); } void World::rechargeItems(double duration, bool activeOnly) @@ -3418,7 +3386,7 @@ namespace MWWorld } } else - mWorldModel.recharge(duration); + mWorldModel.forEachLoadedCellStore([duration](CellStore& store) { store.recharge(duration); }); } void World::teleportToClosestMarker(const MWWorld::Ptr& ptr, const ESM::RefId& id) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 3a1759e595..1aaefe20c7 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -246,9 +246,6 @@ namespace MWWorld bool isCellQuasiExterior() const override; - osg::Vec2f getNorthVector(const CellStore* cell) override; - ///< get north vector for given interior cell - void getDoorMarkers(MWWorld::CellStore* cell, std::vector& out) override; ///< get a list of teleport door markers for a given cell, to be displayed on the local map @@ -358,9 +355,6 @@ namespace MWWorld bool changeEvent = true) override; ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes - const ESM::Cell* getExterior(const ESM::RefId& cellName) const override; - ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. - MWWorld::Ptr getFacedObject() override; ///< Return pointer to the object the player is looking at, if it is within activation range diff --git a/apps/openmw/mwworld/worldmodel.cpp b/apps/openmw/mwworld/worldmodel.cpp index 85ab83cda3..76e71e20e0 100644 --- a/apps/openmw/mwworld/worldmodel.cpp +++ b/apps/openmw/mwworld/worldmodel.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,7 @@ #include "../mwbase/world.hpp" #include "cellstore.hpp" +#include "cellutils.hpp" #include "esmstore.hpp" namespace @@ -85,12 +87,36 @@ MWWorld::CellStore* MWWorld::WorldModel::getCellStore(const ESM::Cell* cell) void MWWorld::WorldModel::clear() { + mPtrIndex.clear(); + mPtrIndexUpdateCounter = 0; + mLastGeneratedRefnum.unset(); mInteriors.clear(); mExteriors.clear(); std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair(ESM::RefId::sEmpty, (MWWorld::CellStore*)nullptr)); mIdCacheIndex = 0; } +void MWWorld::WorldModel::registerPtr(const MWWorld::Ptr& ptr) +{ + mPtrIndex[ptr.getCellRef().getOrAssignRefNum(mLastGeneratedRefnum)] = ptr; + mPtrIndexUpdateCounter++; +} + +void MWWorld::WorldModel::deregisterPtr(const MWWorld::Ptr& ptr) +{ + mPtrIndex.erase(ptr.getCellRef().getRefNum()); + mPtrIndexUpdateCounter++; +} + +MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefNum& refNum) const +{ + auto it = mPtrIndex.find(refNum); + if (it != mPtrIndex.end()) + return it->second; + else + return MWWorld::Ptr(); +} + MWWorld::Ptr MWWorld::WorldModel::getPtrAndCache(const ESM::RefId& name, CellStore& cellStore) { Ptr ptr = getPtr(name, cellStore); @@ -188,32 +214,6 @@ MWWorld::CellStore* MWWorld::WorldModel::getInterior(const ESM::RefId& name) return &result->second; } -void MWWorld::WorldModel::rest(double hours) -{ - for (auto& interior : mInteriors) - { - interior.second.rest(hours); - } - - for (auto& exterior : mExteriors) - { - exterior.second.rest(hours); - } -} - -void MWWorld::WorldModel::recharge(float duration) -{ - for (auto& interior : mInteriors) - { - interior.second.recharge(duration); - } - - for (auto& exterior : mExteriors) - { - exterior.second.recharge(duration); - } -} - MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::CellId& id) { if (id.mPaged) @@ -222,6 +222,57 @@ MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::CellId& id) return getInterior(id.mWorldspace); } +const ESM::Cell* MWWorld::WorldModel::getESMCellByName(const ESM::RefId& name) +{ + const ESM::Cell* cell = mStore.get().search(name); // first try interiors + if (!cell) // try named exteriors + cell = mStore.get().searchExtByName(name); + if (!cell) + { + // treat "Wilderness" like an empty string + static const ESM::RefId defaultName + = ESM::RefId::stringRefId(mStore.get().find("sDefaultCellname")->mValue.getString()); + if (name == defaultName) + cell = mStore.get().searchExtByName(ESM::RefId::sEmpty); + } + if (!cell) + { + // now check for regions + for (const ESM::Region& region : mStore.get()) + { + if (name == ESM::RefId::stringRefId(region.mName)) + { + cell = mStore.get().searchExtByRegion(region.mId); + break; + } + } + } + if (!cell) + throw std::runtime_error(std::string("Can't find cell with name ") + name.getRefIdString()); + return cell; +} + +MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::RefId& name) +{ + const ESM::Cell* cell = getESMCellByName(name); + if (cell->isExterior()) + return getExterior(cell->getGridX(), cell->getGridY()); + else + return getInterior(name); +} + +MWWorld::CellStore* MWWorld::WorldModel::getCellByPosition( + const osg::Vec3f& pos, const ESM::RefId& cellNameInSameWorldSpace) +{ + if (cellNameInSameWorldSpace.empty() || getESMCellByName(cellNameInSameWorldSpace)->isExterior()) + { + const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y()); + return getExterior(cellIndex.x(), cellIndex.y()); + } + else + return getInterior(cellNameInSameWorldSpace); +} + MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name, CellStore& cell, bool searchInContainers) { if (cell.getState() == CellStore::State_Unloaded) diff --git a/apps/openmw/mwworld/worldmodel.hpp b/apps/openmw/mwworld/worldmodel.hpp index 2ec569ce90..204302667a 100644 --- a/apps/openmw/mwworld/worldmodel.hpp +++ b/apps/openmw/mwworld/worldmodel.hpp @@ -4,7 +4,9 @@ #include #include #include +#include +#include "cellstore.hpp" #include "ptr.hpp" namespace ESM @@ -40,6 +42,7 @@ namespace MWWorld WorldModel(const WorldModel&); WorldModel& operator=(const WorldModel&); + const ESM::Cell* getESMCellByName(const ESM::RefId& name); CellStore* getCellStore(const ESM::Cell* cell); Ptr getPtrAndCache(const ESM::RefId& name, CellStore& cellStore); @@ -48,17 +51,34 @@ namespace MWWorld void writeCell(ESM::ESMWriter& writer, CellStore& cell) const; + std::unordered_map mPtrIndex; + size_t mPtrIndexUpdateCounter; + ESM::RefNum mLastGeneratedRefnum; + public: void clear(); explicit WorldModel(const MWWorld::ESMStore& store, ESM::ReadersCache& reader); CellStore* getExterior(int x, int y); - CellStore* getInterior(const ESM::RefId& name); - + CellStore* getCell(const ESM::RefId& name); // interior or named exterior CellStore* getCell(const ESM::CellId& id); + // If cellNameInSameWorldSpace is an interior - returns this interior. + // Otherwise returns exterior cell for given position in the same world space. + // At the moment multiple world spaces are not supported, so all exteriors are in one world space. + CellStore* getCellByPosition(const osg::Vec3f& pos, const ESM::RefId& cellNameInSameWorldSpace); + + void registerPtr(const MWWorld::Ptr& ptr); + void deregisterPtr(const MWWorld::Ptr& ptr); + ESM::RefNum getLastGeneratedRefNum() const { return mLastGeneratedRefnum; } + void setLastGeneratedRefNum(ESM::RefNum v) { mLastGeneratedRefnum = v; } + size_t getPtrIndexUpdateCounter() const { return mPtrIndexUpdateCounter; } + const std::unordered_map& getAllPtrs() const { return mPtrIndex; } + + Ptr getPtr(const ESM::RefNum& refNum) const; + Ptr getPtr(const ESM::RefId& name, CellStore& cellStore, bool searchInContainers = false); ///< \param searchInContainers Only affect loaded cells. /// @note name must be lower case @@ -68,8 +88,14 @@ namespace MWWorld Ptr getPtr(const ESM::RefId& id, const ESM::RefNum& refNum); - void rest(double hours); - void recharge(float duration); + template + void forEachLoadedCellStore(Fn&& fn) + { + for (auto& [_, store] : mInteriors) + fn(store); + for (auto& [_, store] : mExteriors) + fn(store); + } /// Get all Ptrs referencing \a name in exterior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. diff --git a/components/esm3/cellref.hpp b/components/esm3/cellref.hpp index b1b3ebed31..b54d96203e 100644 --- a/components/esm3/cellref.hpp +++ b/components/esm3/cellref.hpp @@ -135,4 +135,22 @@ namespace ESM } +namespace std +{ + + // Needed to use ESM::RefNum as a key in std::unordered_map + template <> + struct hash + { + size_t operator()(const ESM::RefNum& refNum) const + { + assert(sizeof(ESM::RefNum) == sizeof(size_t)); + size_t s; + memcpy(&s, &refNum, sizeof(size_t)); + return hash()(s); + } + }; + +} + #endif