diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 15a6b8713c..f9f4905421 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -59,7 +59,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwlua - luamanagerimp object worldview userdataserializer eventqueue + luamanagerimp object worldview userdataserializer eventqueue objectvariant luabindings localscripts playerscripts objectbindings cellbindings camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings types/types types/door types/actor types/container types/weapon types/npc types/creature types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index 883afb5328..1b9984fc66 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -33,7 +33,7 @@ namespace sol { }; template <> - struct is_automagical : std::false_type + struct is_automagical : std::false_type { }; template <> @@ -198,7 +198,7 @@ namespace MWLua } LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj) - : LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id())) + : LuaUtil::ScriptsContainer(lua, "L" + obj.id().toString()) , mData(obj) { this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData)); diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index 26c8668b5a..bad7300849 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -16,6 +16,46 @@ namespace MWLua { struct Context; + struct SelfObject : public LObject + { + class CachedStat + { + public: + using Setter = void (*)(int, std::string_view, const MWWorld::Ptr&, const sol::object&); + + private: + Setter mSetter; // Function that updates a stat's property + int mIndex; // Optional index to disambiguate the stat + std::string_view mProp; // Name of the stat's property + public: + CachedStat(Setter setter, int index, std::string_view prop) + : mSetter(setter) + , mIndex(index) + , mProp(std::move(prop)) + { + } + + void operator()(const MWWorld::Ptr& ptr, const sol::object& object) const + { + mSetter(mIndex, mProp, ptr, object); + } + + bool operator<(const CachedStat& other) const + { + return std::tie(mSetter, mIndex, mProp) < std::tie(other.mSetter, other.mIndex, other.mProp); + } + }; + + SelfObject(const LObject& obj) + : LObject(obj) + , mIsActive(false) + { + } + MWBase::LuaManager::ActorControls mControls; + std::map mStatsCache; + bool mIsActive; + }; + class LocalScripts : public LuaUtil::ScriptsContainer { public: @@ -25,46 +65,6 @@ namespace MWLua MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; } const MWWorld::Ptr& getPtr() const { return mData.ptr(); } - struct SelfObject : public LObject - { - class CachedStat - { - public: - using Setter = void (*)(int, std::string_view, const MWWorld::Ptr&, const sol::object&); - - private: - Setter mSetter; // Function that updates a stat's property - int mIndex; // Optional index to disambiguate the stat - std::string_view mProp; // Name of the stat's property - public: - CachedStat(Setter setter, int index, std::string_view prop) - : mSetter(setter) - , mIndex(index) - , mProp(std::move(prop)) - { - } - - void operator()(const MWWorld::Ptr& ptr, const sol::object& object) const - { - mSetter(mIndex, mProp, ptr, object); - } - - bool operator<(const CachedStat& other) const - { - return std::tie(mSetter, mIndex, mProp) < std::tie(other.mSetter, other.mIndex, other.mProp); - } - }; - - SelfObject(const LObject& obj) - : LObject(obj) - , mIsActive(false) - { - } - MWBase::LuaManager::ActorControls mControls; - std::map mStatsCache; - bool mIsActive; - }; - struct OnActive { }; diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 9bbd63ac25..6ab9a27581 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -187,11 +187,12 @@ namespace MWLua for (LocalEvent& e : localEvents) { LObject obj(e.mDest); - LocalScripts* scripts = obj.isValid() ? obj.ptr().getRefData().getLuaScripts() : nullptr; + const MWWorld::Ptr& ptr = obj.ptrOrNull(); + LocalScripts* scripts = ptr.isEmpty() ? nullptr : ptr.getRefData().getLuaScripts(); if (scripts) scripts->receiveEvent(e.mEventName, e.mEventData); else - Log(Debug::Debug) << "Ignored event " << e.mEventName << " to L" << idToString(e.mDest) + Log(Debug::Debug) << "Ignored event " << e.mEventName << " to L" << e.mDest.toString() << ". Object not found or has no attached scripts"; } @@ -204,14 +205,15 @@ namespace MWLua for (const LocalEngineEvent& e : mLocalEngineEvents) { LObject obj(e.mDest); - if (!obj.isValid()) + const MWWorld::Ptr& ptr = obj.ptrOrNull(); + if (ptr.isEmpty()) { if (luaDebug) - Log(Debug::Verbose) << "Can not call engine handlers: object" << idToString(e.mDest) + Log(Debug::Verbose) << "Can not call engine handlers: object" << e.mDest.toString() << " is not found"; continue; } - LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); + LocalScripts* scripts = ptr.getRefData().getLuaScripts(); if (scripts) scripts->receiveEngineEvent(e.mEvent); } @@ -238,17 +240,18 @@ namespace MWLua for (ObjectId id : mObjectAddedEvents) { GObject obj(id); - if (obj.isValid()) + const MWWorld::Ptr& ptr = obj.ptrOrNull(); + if (!ptr.isEmpty()) { mGlobalScripts.objectActive(obj); - const MWWorld::Class& objClass = obj.ptr().getClass(); + const MWWorld::Class& objClass = ptr.getClass(); if (objClass.isActor()) mGlobalScripts.actorActive(obj); - if (mWorldView.isItem(obj.ptr())) + if (mWorldView.isItem(ptr)) mGlobalScripts.itemActive(obj); } else if (luaDebug) - Log(Debug::Verbose) << "Could not resolve a Lua object added event: object" << idToString(id) + Log(Debug::Verbose) << "Could not resolve a Lua object added event: object" << id.toString() << " is already removed"; } mObjectAddedEvents.clear(); @@ -675,7 +678,7 @@ namespace MWLua selectedScripts = selectedPtr.getRefData().getLuaScripts(); if (selectedScripts) selectedScripts->collectStats(selectedStats); - out << "Profiled object (selected in the in-game console): " << ptrToString(selectedPtr) << "\n"; + out << "Profiled object (selected in the in-game console): " << selectedPtr.toString() << "\n"; } else out << "No selected object. Use the in-game console to select an object for detailed profile.\n"; diff --git a/apps/openmw/mwlua/object.cpp b/apps/openmw/mwlua/object.cpp deleted file mode 100644 index ed1a9dd9ec..0000000000 --- a/apps/openmw/mwlua/object.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "object.hpp" - -#include "types/types.hpp" - -#include - -namespace MWLua -{ - - std::string idToString(const ObjectId& id) - { - return std::to_string(id.mIndex) + "_" + std::to_string(id.mContentFile); - } - - bool isMarker(const MWWorld::Ptr& ptr) - { - return Misc::ResourceHelpers::isHiddenMarker(ptr.getCellRef().getRefId()); - } - - std::string ptrToString(const MWWorld::Ptr& ptr) - { - std::string res = "object"; - if (ptr.getRefData().isDeleted()) - res = "deleted object"; - res.append(idToString(getId(ptr))); - res.append(" ("); - res.append(getLuaObjectTypeName(ptr)); - res.append(", "); - res.append(ptr.getCellRef().getRefId().getRefIdString()); - res.append(")"); - return res; - } - - std::string Object::toString() const - { - if (isValid()) - return ptrToString(ptr()); - else - return "object" + idToString(mId) + " (not found)"; - } - - bool Object::isValid() const - { - MWWorld::WorldModel& w = *MWBase::Environment::get().getWorldModel(); - if (mLastUpdate < w.getPtrIndexUpdateCounter()) - { - mPtr = w.getPtr(mId); - mLastUpdate = w.getPtrIndexUpdateCounter(); - } - return !mPtr.isEmpty(); - } - - const MWWorld::Ptr& Object::ptr() const - { - if (!isValid()) - throw std::runtime_error("Object is not available: " + idToString(mId)); - return mPtr; - } -} diff --git a/apps/openmw/mwlua/object.hpp b/apps/openmw/mwlua/object.hpp index 9c720c67ce..9f4075582a 100644 --- a/apps/openmw/mwlua/object.hpp +++ b/apps/openmw/mwlua/object.hpp @@ -21,39 +21,24 @@ namespace MWLua { return ptr.getCellRef().getRefNum(); } - std::string idToString(const ObjectId& id); - std::string ptrToString(const MWWorld::Ptr& ptr); - bool isMarker(const MWWorld::Ptr& ptr); // 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 + class Object : public MWWorld::SafePtr { public: - Object(ObjectId id) - : mId(id) - { - } - virtual ~Object() {} - ObjectId id() const { return mId; } - - std::string toString() const; - - // Updates and returns the underlying Ptr. Throws an exception if object is not available. - const MWWorld::Ptr& ptr() const; - - // Returns `true` if calling `ptr()` is safe. - bool isValid() const; - - virtual sol::object getObject(lua_State* lua, ObjectId id) const = 0; // returns LObject or GOBject + using SafePtr::SafePtr; + virtual sol::object getObject(lua_State* lua, ObjectId id) const = 0; // returns LObject or GObject virtual sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const = 0; // returns LCell or GCell - protected: - const ObjectId mId; - - mutable MWWorld::Ptr mPtr; - mutable size_t mLastUpdate = 0; + const MWWorld::Ptr& ptr() const + { + const MWWorld::Ptr& res = ptrOrNull(); + if (res.isEmpty()) + throw std::runtime_error("Object is not available: " + id().toString()); + return res; + } }; // Used only in local scripts @@ -100,7 +85,6 @@ namespace MWLua { Obj mObj; }; - } #endif // MWLUA_OBJECT_H diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 5b67b9f718..f92906f54e 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -116,10 +116,10 @@ namespace MWLua { MWWorld::Ptr object = MWBase::Environment::get().getWorldModel()->getPtr(mObject); if (object.isEmpty()) - throw std::runtime_error(std::string("Object not found: " + idToString(mObject))); + throw std::runtime_error(std::string("Object not found: " + mObject.toString())); MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor); if (actor.isEmpty()) - throw std::runtime_error(std::string("Actor not found: " + idToString(mActor))); + throw std::runtime_error(std::string("Actor not found: " + mActor.toString())); if (object.getRefData().activate()) { @@ -131,8 +131,8 @@ namespace MWLua std::string toString() const override { - return std::string("ActivateAction object=") + idToString(mObject) + std::string(" actor=") - + idToString(mActor); + return std::string("ActivateAction object=") + mObject.toString() + std::string(" actor=") + + mActor.toString(); } private: @@ -165,7 +165,7 @@ namespace MWLua template void addBasicBindings(sol::usertype& objectT, const Context& context) { - objectT["isValid"] = [](const ObjectT& o) { return o.isValid(); }; + objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); }; objectT["recordId"] = sol::readonly_property( [](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().getRefIdString(); }); objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional> { @@ -197,7 +197,7 @@ namespace MWLua if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_) throw std::runtime_error( "The argument of `activateBy` must be an actor who activates the object. Got: " - + ptrToString(actor.ptr())); + + actor.toString()); context.mLuaManager->addAction(std::make_unique(context.mLua, o.id(), actor.id())); }; @@ -266,7 +266,7 @@ namespace MWLua MWWorld::Ptr ptr = object.ptr(); LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); if (!localScripts || !localScripts->hasScript(*scriptId)) - throw std::runtime_error("There is no script " + std::string(path) + " on " + ptrToString(ptr)); + throw std::runtime_error("There is no script " + std::string(path) + " on " + ptr.toString()); if (localScripts->getAutoStartConf().count(*scriptId) > 0) throw std::runtime_error("Autostarted script can not be removed: " + std::string(path)); localScripts->removeScript(*scriptId); diff --git a/apps/openmw/mwlua/objectvariant.hpp b/apps/openmw/mwlua/objectvariant.hpp new file mode 100644 index 0000000000..8be0a390ea --- /dev/null +++ b/apps/openmw/mwlua/objectvariant.hpp @@ -0,0 +1,55 @@ +#ifndef MWLUA_OBJECTVARIANT_H +#define MWLUA_OBJECTVARIANT_H + +#include + +#include "localscripts.hpp" +#include "object.hpp" + +namespace MWLua +{ + + class ObjectVariant + { + public: + explicit ObjectVariant(const sol::object& obj) + { + if (obj.is()) + mVariant.emplace(obj.as()); + else if (obj.is()) + mVariant.emplace(obj.as()); + else + mVariant.emplace(obj.as()); + } + + bool isSelfObject() const { return std::holds_alternative(mVariant); } + bool isLObject() const { return std::holds_alternative(mVariant); } + bool isGObject() const { return std::holds_alternative(mVariant); } + + SelfObject* asSelfObject() const + { + if (!isSelfObject()) + throw std::runtime_error("Allowed only in local scripts for 'openmw.self'."); + return std::get(mVariant); + } + + const MWWorld::Ptr& ptr() const + { + return std::visit( + [](auto&& variant) -> const MWWorld::Ptr& { + using T = std::decay_t; + if constexpr (std::is_same_v) + return variant->ptr(); + else + return variant.ptr(); + }, + mVariant); + } + + private: + std::variant mVariant; + }; + +} // namespace MWLua + +#endif // MWLUA_OBJECTVARIANT_H diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 8740ae15ee..2def0c6ac6 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -18,14 +18,17 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" +#include "objectvariant.hpp" + namespace { + using SelfObject = MWLua::SelfObject; + using ObjectVariant = MWLua::ObjectVariant; + template auto addIndexedAccessor(int index) { - return sol::overload([index](MWLua::LocalScripts::SelfObject& o) { return T::create(&o, index); }, - [index](const MWLua::LObject& o) { return T::create(o, index); }, - [index](const MWLua::GObject& o) { return T::create(o, index); }); + return [index](const sol::object& o) { return T::create(ObjectVariant(o), index); }; } template @@ -35,50 +38,18 @@ namespace [=](const T& stat, const sol::object& value) { stat.cache(context, prop, value); }); } - using SelfObject = MWLua::LocalScripts::SelfObject; - using StatObject = std::variant; - SelfObject* asSelfObject(const StatObject& obj) - { - if (!std::holds_alternative(obj)) - throw std::runtime_error("Changing stats allowed only in local scripts for 'openmw.self'."); - return std::get(obj); - } - - const MWLua::Object* getObject(const StatObject& obj) - { - return std::visit( - [](auto&& variant) -> const MWLua::Object* { - using T = std::decay_t; - if constexpr (std::is_same_v) - return variant; - else if constexpr (std::is_same_v) - return &variant; - else if constexpr (std::is_same_v) - return &variant; - }, - obj); - } - template - sol::object getValue(const MWLua::Context& context, const StatObject& obj, SelfObject::CachedStat::Setter setter, + sol::object getValue(const MWLua::Context& context, const ObjectVariant& obj, SelfObject::CachedStat::Setter setter, int index, std::string_view prop, G getter) { - return std::visit( - [&](auto&& variant) { - using T = std::decay_t; - if constexpr (std::is_same_v) - { - auto it = variant->mStatsCache.find({ setter, index, prop }); - if (it != variant->mStatsCache.end()) - return it->second; - return sol::make_object(context.mLua->sol(), getter(variant)); - } - else if constexpr (std::is_same_v) - return sol::make_object(context.mLua->sol(), getter(&variant)); - else if constexpr (std::is_same_v) - return sol::make_object(context.mLua->sol(), getter(&variant)); - }, - obj); + if (obj.isSelfObject()) + { + SelfObject* self = obj.asSelfObject(); + auto it = self->mStatsCache.find({ setter, index, prop }); + if (it != self->mStatsCache.end()) + return it->second; + } + return sol::make_object(context.mLua->sol(), getter(obj.ptr())); } } @@ -111,9 +82,9 @@ namespace MWLua class LevelStat { - StatObject mObject; + ObjectVariant mObject; - LevelStat(StatObject object) + LevelStat(ObjectVariant object) : mObject(std::move(object)) { } @@ -121,15 +92,13 @@ namespace MWLua public: sol::object getCurrent(const Context& context) const { - return getValue(context, mObject, &LevelStat::setValue, 0, "current", [](const MWLua::Object* obj) { - const auto& ptr = obj->ptr(); - return ptr.getClass().getCreatureStats(ptr).getLevel(); - }); + return getValue(context, mObject, &LevelStat::setValue, 0, "current", + [](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); }); } void setCurrent(const Context& context, const sol::object& value) const { - SelfObject* obj = asSelfObject(mObject); + SelfObject* obj = mObject.asSelfObject(); if (obj->mStatsCache.empty()) context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value; @@ -137,15 +106,15 @@ namespace MWLua sol::object getProgress(const Context& context) const { - const auto& ptr = getObject(mObject)->ptr(); + const auto& ptr = mObject.ptr(); if (!ptr.getClass().isNpc()) return sol::nil; return sol::make_object(context.mLua->sol(), ptr.getClass().getNpcStats(ptr).getLevelProgress()); } - static std::optional create(StatObject object, int index) + static std::optional create(ObjectVariant object, int index) { - if (!getObject(object)->ptr().getClass().isActor()) + if (!object.ptr().getClass().isActor()) return {}; return LevelStat{ std::move(object) }; } @@ -160,10 +129,10 @@ namespace MWLua class DynamicStat { - StatObject mObject; + ObjectVariant mObject; int mIndex; - DynamicStat(StatObject object, int index) + DynamicStat(ObjectVariant object, int index) : mObject(std::move(object)) , mIndex(index) { @@ -174,22 +143,21 @@ namespace MWLua sol::object get(const Context& context, std::string_view prop, G getter) const { return getValue( - context, mObject, &DynamicStat::setValue, mIndex, prop, [this, getter](const MWLua::Object* obj) { - const auto& ptr = obj->ptr(); + context, mObject, &DynamicStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) { return (ptr.getClass().getCreatureStats(ptr).getDynamic(mIndex).*getter)(); }); } - static std::optional create(StatObject object, int index) + static std::optional create(ObjectVariant object, int index) { - if (!getObject(object)->ptr().getClass().isActor()) + if (!object.ptr().getClass().isActor()) return {}; return DynamicStat{ std::move(object), index }; } void cache(const Context& context, std::string_view prop, const sol::object& value) const { - SelfObject* obj = asSelfObject(mObject); + SelfObject* obj = mObject.asSelfObject(); if (obj->mStatsCache.empty()) context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value; @@ -212,10 +180,10 @@ namespace MWLua class AttributeStat { - StatObject mObject; + ObjectVariant mObject; int mIndex; - AttributeStat(StatObject object, int index) + AttributeStat(ObjectVariant object, int index) : mObject(std::move(object)) , mIndex(index) { @@ -226,8 +194,7 @@ namespace MWLua sol::object get(const Context& context, std::string_view prop, G getter) const { return getValue( - context, mObject, &AttributeStat::setValue, mIndex, prop, [this, getter](const MWLua::Object* obj) { - const auto& ptr = obj->ptr(); + context, mObject, &AttributeStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) { return (ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex).*getter)(); }); } @@ -240,16 +207,16 @@ namespace MWLua return std::max(0.f, base - damage + modifier); // Should match AttributeValue::getModified } - static std::optional create(StatObject object, int index) + static std::optional create(ObjectVariant object, int index) { - if (!getObject(object)->ptr().getClass().isActor()) + if (!object.ptr().getClass().isActor()) return {}; return AttributeStat{ std::move(object), index }; } void cache(const Context& context, std::string_view prop, const sol::object& value) const { - SelfObject* obj = asSelfObject(mObject); + SelfObject* obj = mObject.asSelfObject(); if (obj->mStatsCache.empty()) context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value; @@ -275,10 +242,10 @@ namespace MWLua class SkillStat { - StatObject mObject; + ObjectVariant mObject; int mIndex; - SkillStat(StatObject object, int index) + SkillStat(ObjectVariant object, int index) : mObject(std::move(object)) , mIndex(index) { @@ -304,8 +271,7 @@ namespace MWLua sol::object get(const Context& context, std::string_view prop, G getter) const { return getValue( - context, mObject, &SkillStat::setValue, mIndex, prop, [this, getter](const MWLua::Object* obj) { - const auto& ptr = obj->ptr(); + context, mObject, &SkillStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) { return (ptr.getClass().getNpcStats(ptr).getSkill(mIndex).*getter)(); }); } @@ -321,22 +287,21 @@ namespace MWLua sol::object getProgress(const Context& context) const { return getValue( - context, mObject, &SkillStat::setValue, mIndex, "progress", [this](const MWLua::Object* obj) { - const auto& ptr = obj->ptr(); + context, mObject, &SkillStat::setValue, mIndex, "progress", [this](const MWWorld::Ptr& ptr) { return getProgress(ptr, mIndex, ptr.getClass().getNpcStats(ptr).getSkill(mIndex)); }); } - static std::optional create(StatObject object, int index) + static std::optional create(ObjectVariant object, int index) { - if (!getObject(object)->ptr().getClass().isNpc()) + if (!object.ptr().getClass().isNpc()) return {}; return SkillStat{ std::move(object), index }; } void cache(const Context& context, std::string_view prop, const sol::object& value) const { - SelfObject* obj = asSelfObject(mObject); + SelfObject* obj = mObject.asSelfObject(); if (obj->mStatsCache.empty()) context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value; diff --git a/apps/openmw/mwlua/types/actor.cpp b/apps/openmw/mwlua/types/actor.cpp index 10634d1812..15e29544ee 100644 --- a/apps/openmw/mwlua/types/actor.cpp +++ b/apps/openmw/mwlua/types/actor.cpp @@ -51,7 +51,7 @@ namespace MWLua || itemPtr.getContainerStore() != static_cast(&store)) { Log(Debug::Warning) - << "Object" << idToString(std::get(item)) << " is not in inventory"; + << "Object" << std::get(item).toString() << " is not in inventory"; return false; } } @@ -77,7 +77,7 @@ namespace MWLua allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; }); if (firstAllowed == allowedSlots.end()) { - Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr); + Log(Debug::Warning) << "No suitable slot for " << itemPtr.toString(); return false; } slot = *firstAllowed; @@ -119,8 +119,6 @@ namespace MWLua }; } - using SelfObject = LocalScripts::SelfObject; - void addActorBindings(sol::table actor, const Context& context) { actor["STANCE"] @@ -261,7 +259,7 @@ namespace MWLua if (!obj.ptr().getClass().hasInventoryStore(obj.ptr())) { if (!equipment.empty()) - throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots"); + throw std::runtime_error(obj.toString() + " has no equipment slots"); return; } SetEquipmentAction::Equipment eqp; diff --git a/apps/openmw/mwlua/types/types.cpp b/apps/openmw/mwlua/types/types.cpp index 2ac72e710e..75cc072ce2 100644 --- a/apps/openmw/mwlua/types/types.cpp +++ b/apps/openmw/mwlua/types/types.cpp @@ -95,7 +95,7 @@ namespace MWLua std::string msg = "Requires type '"; msg.append(getLuaObjectTypeName(recordType)); msg.append("', but applied to "); - msg.append(ptrToString(ptr)); + msg.append(ptr.toString()); throw std::runtime_error(msg); } return ptr; diff --git a/apps/openmw/mwlua/worldview.cpp b/apps/openmw/mwlua/worldview.cpp index cc1d381531..d06420399b 100644 --- a/apps/openmw/mwlua/worldview.cpp +++ b/apps/openmw/mwlua/worldview.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwclass/container.hpp" @@ -39,7 +41,7 @@ namespace MWLua { // It is important to check `isMarker` first. // For example "prisonmarker" has class "Door" despite that it is only an invisible marker. - if (isMarker(ptr)) + if (Misc::ResourceHelpers::isHiddenMarker(ptr.getCellRef().getRefId())) return nullptr; const MWWorld::Class& cls = ptr.getClass(); if (cls.isActivator()) diff --git a/apps/openmw/mwworld/ptr.cpp b/apps/openmw/mwworld/ptr.cpp new file mode 100644 index 0000000000..6ad6f143af --- /dev/null +++ b/apps/openmw/mwworld/ptr.cpp @@ -0,0 +1,42 @@ +#include "ptr.hpp" + +#include "apps/openmw/mwbase/environment.hpp" + +#include "worldmodel.hpp" + +namespace MWWorld +{ + + std::string Ptr::toString() const + { + std::string res = "object"; + if (getRefData().isDeleted()) + res = "deleted object"; + res.append(getCellRef().getRefNum().toString()); + res.append(" ("); + res.append(getTypeDescription()); + res.append(", "); + res.append(getCellRef().getRefId().getRefIdString()); + res.append(")"); + return res; + } + + std::string SafePtr::toString() const + { + update(); + if (mPtr.isEmpty()) + return "object" + mId.toString() + " (not found)"; + else + return mPtr.toString(); + } + + void SafePtr::update() const + { + WorldModel& w = *MWBase::Environment::get().getWorldModel(); + if (mLastUpdate < w.getPtrIndexUpdateCounter()) + { + mPtr = w.getPtr(mId); + mLastUpdate = w.getPtrIndexUpdateCounter(); + } + } +} diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index fdf69b8818..4299130aa8 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -128,6 +128,8 @@ namespace MWWorld : PtrBase(liveCellRef, cell, nullptr) { } + + std::string toString() const; }; /// @note The difference between Ptr and ConstPtr is that the second one adds const to the underlying pointers. @@ -145,6 +147,41 @@ namespace MWWorld } }; + // SafePtr holds Ptr and automatically updates it via WorldModel if the Ptr becomes invalid. + // Uses ESM::RefNum as an unique id. Can not be used for Ptrs without RefNum. + // Note: WorldModel automatically assignes RefNum to all registered Ptrs. + class SafePtr + { + public: + using Id = ESM::RefNum; + + explicit SafePtr(Id id) + : mId(id) + { + } + explicit SafePtr(const Ptr& ptr) + : SafePtr(ptr.getCellRef().getRefNum()) + { + } + virtual ~SafePtr() = default; + Id id() const { return mId; } + + std::string toString() const; + + const Ptr& ptrOrNull() const + { + update(); + return mPtr; + } + + private: + const Id mId; + + mutable Ptr mPtr; + mutable size_t mLastUpdate = 0; + + void update() const; + }; } #endif diff --git a/components/esm3/cellref.cpp b/components/esm3/cellref.cpp index d9cef58a4b..d765497948 100644 --- a/components/esm3/cellref.cpp +++ b/components/esm3/cellref.cpp @@ -175,6 +175,11 @@ namespace ESM } } + std::string RefNum::toString() const + { + return std::to_string(mIndex) + "_" + std::to_string(mContentFile); + } + void CellRef::load(ESMReader& esm, bool& isDeleted, bool wideRefNum) { loadId(esm, wideRefNum); diff --git a/components/esm3/cellref.hpp b/components/esm3/cellref.hpp index 7912c11b69..0ea118178f 100644 --- a/components/esm3/cellref.hpp +++ b/components/esm3/cellref.hpp @@ -27,6 +27,8 @@ namespace ESM inline bool hasContentFile() const { return mContentFile >= 0; } inline bool isSet() const { return mIndex != 0 || mContentFile != -1; } + + std::string toString() const; }; /* Cell reference. This represents ONE object (of many) inside the