1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-26 14:36:48 +00:00

Merge branch 'ptr' into 'master'

MWLua refactoring

See merge request OpenMW/openmw!2803
This commit is contained in:
psi29a 2023-03-16 11:21:45 +00:00
commit df89a8c845
16 changed files with 263 additions and 229 deletions

View file

@ -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

View file

@ -33,7 +33,7 @@ namespace sol
{
};
template <>
struct is_automagical<MWLua::LocalScripts::SelfObject> : std::false_type
struct is_automagical<MWLua::SelfObject> : 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));

View file

@ -16,15 +16,6 @@ namespace MWLua
{
struct Context;
class LocalScripts : public LuaUtil::ScriptsContainer
{
public:
static void initializeSelfPackage(const Context&);
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; }
const MWWorld::Ptr& getPtr() const { return mData.ptr(); }
struct SelfObject : public LObject
{
class CachedStat
@ -65,6 +56,15 @@ namespace MWLua
bool mIsActive;
};
class LocalScripts : public LuaUtil::ScriptsContainer
{
public:
static void initializeSelfPackage(const Context&);
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; }
const MWWorld::Ptr& getPtr() const { return mData.ptr(); }
struct OnActive
{
};

View file

@ -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";

View file

@ -1,59 +0,0 @@
#include "object.hpp"
#include "types/types.hpp"
#include <components/misc/resourcehelpers.hpp>
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;
}
}

View file

@ -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

View file

@ -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 <class ObjectT>
void addBasicBindings(sol::usertype<ObjectT>& 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<Cell<ObjectT>> {
@ -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<ActivateAction>(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);

View file

@ -0,0 +1,55 @@
#ifndef MWLUA_OBJECTVARIANT_H
#define MWLUA_OBJECTVARIANT_H
#include <variant>
#include "localscripts.hpp"
#include "object.hpp"
namespace MWLua
{
class ObjectVariant
{
public:
explicit ObjectVariant(const sol::object& obj)
{
if (obj.is<SelfObject>())
mVariant.emplace<SelfObject*>(obj.as<SelfObject*>());
else if (obj.is<LObject>())
mVariant.emplace<LObject>(obj.as<LObject>());
else
mVariant.emplace<GObject>(obj.as<GObject>());
}
bool isSelfObject() const { return std::holds_alternative<SelfObject*>(mVariant); }
bool isLObject() const { return std::holds_alternative<LObject>(mVariant); }
bool isGObject() const { return std::holds_alternative<GObject>(mVariant); }
SelfObject* asSelfObject() const
{
if (!isSelfObject())
throw std::runtime_error("Allowed only in local scripts for 'openmw.self'.");
return std::get<SelfObject*>(mVariant);
}
const MWWorld::Ptr& ptr() const
{
return std::visit(
[](auto&& variant) -> const MWWorld::Ptr& {
using T = std::decay_t<decltype(variant)>;
if constexpr (std::is_same_v<T, SelfObject*>)
return variant->ptr();
else
return variant.ptr();
},
mVariant);
}
private:
std::variant<SelfObject*, LObject, GObject> mVariant;
};
} // namespace MWLua
#endif // MWLUA_OBJECTVARIANT_H

View file

@ -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 <class T>
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 <class T, class G>
@ -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*, MWLua::LObject, MWLua::GObject>;
SelfObject* asSelfObject(const StatObject& obj)
{
if (!std::holds_alternative<SelfObject*>(obj))
throw std::runtime_error("Changing stats allowed only in local scripts for 'openmw.self'.");
return std::get<SelfObject*>(obj);
}
const MWLua::Object* getObject(const StatObject& obj)
{
return std::visit(
[](auto&& variant) -> const MWLua::Object* {
using T = std::decay_t<decltype(variant)>;
if constexpr (std::is_same_v<T, SelfObject*>)
return variant;
else if constexpr (std::is_same_v<T, MWLua::LObject>)
return &variant;
else if constexpr (std::is_same_v<T, MWLua::GObject>)
return &variant;
},
obj);
}
template <class G>
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<decltype(variant)>;
if constexpr (std::is_same_v<T, SelfObject*>)
if (obj.isSelfObject())
{
auto it = variant->mStatsCache.find({ setter, index, prop });
if (it != variant->mStatsCache.end())
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(variant));
}
else if constexpr (std::is_same_v<T, MWLua::LObject>)
return sol::make_object(context.mLua->sol(), getter(&variant));
else if constexpr (std::is_same_v<T, MWLua::GObject>)
return sol::make_object(context.mLua->sol(), getter(&variant));
},
obj);
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<StatUpdateAction>(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<LevelStat> create(StatObject object, int index)
static std::optional<LevelStat> 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<DynamicStat> create(StatObject object, int index)
static std::optional<DynamicStat> 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<StatUpdateAction>(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<AttributeStat> create(StatObject object, int index)
static std::optional<AttributeStat> 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<StatUpdateAction>(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<SkillStat> create(StatObject object, int index)
static std::optional<SkillStat> 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<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value;

View file

@ -51,7 +51,7 @@ namespace MWLua
|| itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
{
Log(Debug::Warning)
<< "Object" << idToString(std::get<ObjectId>(item)) << " is not in inventory";
<< "Object" << std::get<ObjectId>(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;

View file

@ -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;

View file

@ -4,6 +4,8 @@
#include <components/esm3/esmwriter.hpp>
#include <components/esm3/loadcell.hpp>
#include <components/misc/resourcehelpers.hpp>
#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())

View file

@ -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();
}
}
}

View file

@ -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

View file

@ -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);

View file

@ -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