1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-26 21:06:49 +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 add_openmw_dir (mwlua
luamanagerimp object worldview userdataserializer eventqueue luamanagerimp object worldview userdataserializer eventqueue objectvariant
luabindings localscripts playerscripts objectbindings cellbindings luabindings localscripts playerscripts objectbindings cellbindings
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings 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 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 <> template <>
struct is_automagical<MWLua::LocalScripts::SelfObject> : std::false_type struct is_automagical<MWLua::SelfObject> : std::false_type
{ {
}; };
template <> template <>
@ -198,7 +198,7 @@ namespace MWLua
} }
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj) LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj)
: LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id())) : LuaUtil::ScriptsContainer(lua, "L" + obj.id().toString())
, mData(obj) , mData(obj)
{ {
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData)); this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));

View file

@ -16,15 +16,6 @@ namespace MWLua
{ {
struct Context; 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 struct SelfObject : public LObject
{ {
class CachedStat class CachedStat
@ -65,6 +56,15 @@ namespace MWLua
bool mIsActive; 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 struct OnActive
{ {
}; };

View file

@ -187,11 +187,12 @@ namespace MWLua
for (LocalEvent& e : localEvents) for (LocalEvent& e : localEvents)
{ {
LObject obj(e.mDest); 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) if (scripts)
scripts->receiveEvent(e.mEventName, e.mEventData); scripts->receiveEvent(e.mEventName, e.mEventData);
else 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"; << ". Object not found or has no attached scripts";
} }
@ -204,14 +205,15 @@ namespace MWLua
for (const LocalEngineEvent& e : mLocalEngineEvents) for (const LocalEngineEvent& e : mLocalEngineEvents)
{ {
LObject obj(e.mDest); LObject obj(e.mDest);
if (!obj.isValid()) const MWWorld::Ptr& ptr = obj.ptrOrNull();
if (ptr.isEmpty())
{ {
if (luaDebug) 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"; << " is not found";
continue; continue;
} }
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); LocalScripts* scripts = ptr.getRefData().getLuaScripts();
if (scripts) if (scripts)
scripts->receiveEngineEvent(e.mEvent); scripts->receiveEngineEvent(e.mEvent);
} }
@ -238,17 +240,18 @@ namespace MWLua
for (ObjectId id : mObjectAddedEvents) for (ObjectId id : mObjectAddedEvents)
{ {
GObject obj(id); GObject obj(id);
if (obj.isValid()) const MWWorld::Ptr& ptr = obj.ptrOrNull();
if (!ptr.isEmpty())
{ {
mGlobalScripts.objectActive(obj); mGlobalScripts.objectActive(obj);
const MWWorld::Class& objClass = obj.ptr().getClass(); const MWWorld::Class& objClass = ptr.getClass();
if (objClass.isActor()) if (objClass.isActor())
mGlobalScripts.actorActive(obj); mGlobalScripts.actorActive(obj);
if (mWorldView.isItem(obj.ptr())) if (mWorldView.isItem(ptr))
mGlobalScripts.itemActive(obj); mGlobalScripts.itemActive(obj);
} }
else if (luaDebug) 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"; << " is already removed";
} }
mObjectAddedEvents.clear(); mObjectAddedEvents.clear();
@ -675,7 +678,7 @@ namespace MWLua
selectedScripts = selectedPtr.getRefData().getLuaScripts(); selectedScripts = selectedPtr.getRefData().getLuaScripts();
if (selectedScripts) if (selectedScripts)
selectedScripts->collectStats(selectedStats); 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 else
out << "No selected object. Use the in-game console to select an object for detailed profile.\n"; 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(); 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. // 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. // `GObject` and `LObject` are intended to be passed to Lua as a userdata.
// It automatically updates the underlying Ptr when needed. // It automatically updates the underlying Ptr when needed.
class Object class Object : public MWWorld::SafePtr
{ {
public: public:
Object(ObjectId id) using SafePtr::SafePtr;
: mId(id) virtual sol::object getObject(lua_State* lua, ObjectId id) const = 0; // returns LObject or GObject
{
}
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
virtual sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const = 0; // returns LCell or GCell virtual sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const = 0; // returns LCell or GCell
protected: const MWWorld::Ptr& ptr() const
const ObjectId mId; {
const MWWorld::Ptr& res = ptrOrNull();
mutable MWWorld::Ptr mPtr; if (res.isEmpty())
mutable size_t mLastUpdate = 0; throw std::runtime_error("Object is not available: " + id().toString());
return res;
}
}; };
// Used only in local scripts // Used only in local scripts
@ -100,7 +85,6 @@ namespace MWLua
{ {
Obj mObj; Obj mObj;
}; };
} }
#endif // MWLUA_OBJECT_H #endif // MWLUA_OBJECT_H

View file

@ -116,10 +116,10 @@ namespace MWLua
{ {
MWWorld::Ptr object = MWBase::Environment::get().getWorldModel()->getPtr(mObject); MWWorld::Ptr object = MWBase::Environment::get().getWorldModel()->getPtr(mObject);
if (object.isEmpty()) 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); MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor);
if (actor.isEmpty()) 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()) if (object.getRefData().activate())
{ {
@ -131,8 +131,8 @@ namespace MWLua
std::string toString() const override std::string toString() const override
{ {
return std::string("ActivateAction object=") + idToString(mObject) + std::string(" actor=") return std::string("ActivateAction object=") + mObject.toString() + std::string(" actor=")
+ idToString(mActor); + mActor.toString();
} }
private: private:
@ -165,7 +165,7 @@ namespace MWLua
template <class ObjectT> template <class ObjectT>
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context) 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( objectT["recordId"] = sol::readonly_property(
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().getRefIdString(); }); [](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().getRefIdString(); });
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> { 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_) if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_)
throw std::runtime_error( throw std::runtime_error(
"The argument of `activateBy` must be an actor who activates the object. Got: " "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())); context.mLuaManager->addAction(std::make_unique<ActivateAction>(context.mLua, o.id(), actor.id()));
}; };
@ -266,7 +266,7 @@ namespace MWLua
MWWorld::Ptr ptr = object.ptr(); MWWorld::Ptr ptr = object.ptr();
LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
if (!localScripts || !localScripts->hasScript(*scriptId)) 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) if (localScripts->getAutoStartConf().count(*scriptId) > 0)
throw std::runtime_error("Autostarted script can not be removed: " + std::string(path)); throw std::runtime_error("Autostarted script can not be removed: " + std::string(path));
localScripts->removeScript(*scriptId); 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/class.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "objectvariant.hpp"
namespace namespace
{ {
using SelfObject = MWLua::SelfObject;
using ObjectVariant = MWLua::ObjectVariant;
template <class T> template <class T>
auto addIndexedAccessor(int index) auto addIndexedAccessor(int index)
{ {
return sol::overload([index](MWLua::LocalScripts::SelfObject& o) { return T::create(&o, index); }, return [index](const sol::object& o) { return T::create(ObjectVariant(o), index); };
[index](const MWLua::LObject& o) { return T::create(o, index); },
[index](const MWLua::GObject& o) { return T::create(o, index); });
} }
template <class T, class G> template <class T, class G>
@ -35,50 +38,18 @@ namespace
[=](const T& stat, const sol::object& value) { stat.cache(context, prop, value); }); [=](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> 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) int index, std::string_view prop, G getter)
{ {
return std::visit( if (obj.isSelfObject())
[&](auto&& variant) {
using T = std::decay_t<decltype(variant)>;
if constexpr (std::is_same_v<T, SelfObject*>)
{ {
auto it = variant->mStatsCache.find({ setter, index, prop }); SelfObject* self = obj.asSelfObject();
if (it != variant->mStatsCache.end()) auto it = self->mStatsCache.find({ setter, index, prop });
if (it != self->mStatsCache.end())
return it->second; 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(obj.ptr()));
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);
} }
} }
@ -111,9 +82,9 @@ namespace MWLua
class LevelStat class LevelStat
{ {
StatObject mObject; ObjectVariant mObject;
LevelStat(StatObject object) LevelStat(ObjectVariant object)
: mObject(std::move(object)) : mObject(std::move(object))
{ {
} }
@ -121,15 +92,13 @@ namespace MWLua
public: public:
sol::object getCurrent(const Context& context) const sol::object getCurrent(const Context& context) const
{ {
return getValue(context, mObject, &LevelStat::setValue, 0, "current", [](const MWLua::Object* obj) { return getValue(context, mObject, &LevelStat::setValue, 0, "current",
const auto& ptr = obj->ptr(); [](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); });
return ptr.getClass().getCreatureStats(ptr).getLevel();
});
} }
void setCurrent(const Context& context, const sol::object& value) const void setCurrent(const Context& context, const sol::object& value) const
{ {
SelfObject* obj = asSelfObject(mObject); SelfObject* obj = mObject.asSelfObject();
if (obj->mStatsCache.empty()) if (obj->mStatsCache.empty())
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id())); context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value; obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value;
@ -137,15 +106,15 @@ namespace MWLua
sol::object getProgress(const Context& context) const sol::object getProgress(const Context& context) const
{ {
const auto& ptr = getObject(mObject)->ptr(); const auto& ptr = mObject.ptr();
if (!ptr.getClass().isNpc()) if (!ptr.getClass().isNpc())
return sol::nil; return sol::nil;
return sol::make_object(context.mLua->sol(), ptr.getClass().getNpcStats(ptr).getLevelProgress()); 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 {};
return LevelStat{ std::move(object) }; return LevelStat{ std::move(object) };
} }
@ -160,10 +129,10 @@ namespace MWLua
class DynamicStat class DynamicStat
{ {
StatObject mObject; ObjectVariant mObject;
int mIndex; int mIndex;
DynamicStat(StatObject object, int index) DynamicStat(ObjectVariant object, int index)
: mObject(std::move(object)) : mObject(std::move(object))
, mIndex(index) , mIndex(index)
{ {
@ -174,22 +143,21 @@ namespace MWLua
sol::object get(const Context& context, std::string_view prop, G getter) const sol::object get(const Context& context, std::string_view prop, G getter) const
{ {
return getValue( return getValue(
context, mObject, &DynamicStat::setValue, mIndex, prop, [this, getter](const MWLua::Object* obj) { context, mObject, &DynamicStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) {
const auto& ptr = obj->ptr();
return (ptr.getClass().getCreatureStats(ptr).getDynamic(mIndex).*getter)(); 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 {};
return DynamicStat{ std::move(object), index }; return DynamicStat{ std::move(object), index };
} }
void cache(const Context& context, std::string_view prop, const sol::object& value) const 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()) if (obj->mStatsCache.empty())
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id())); context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value; obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value;
@ -212,10 +180,10 @@ namespace MWLua
class AttributeStat class AttributeStat
{ {
StatObject mObject; ObjectVariant mObject;
int mIndex; int mIndex;
AttributeStat(StatObject object, int index) AttributeStat(ObjectVariant object, int index)
: mObject(std::move(object)) : mObject(std::move(object))
, mIndex(index) , mIndex(index)
{ {
@ -226,8 +194,7 @@ namespace MWLua
sol::object get(const Context& context, std::string_view prop, G getter) const sol::object get(const Context& context, std::string_view prop, G getter) const
{ {
return getValue( return getValue(
context, mObject, &AttributeStat::setValue, mIndex, prop, [this, getter](const MWLua::Object* obj) { context, mObject, &AttributeStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) {
const auto& ptr = obj->ptr();
return (ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex).*getter)(); 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 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 {};
return AttributeStat{ std::move(object), index }; return AttributeStat{ std::move(object), index };
} }
void cache(const Context& context, std::string_view prop, const sol::object& value) const 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()) if (obj->mStatsCache.empty())
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id())); context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value; obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value;
@ -275,10 +242,10 @@ namespace MWLua
class SkillStat class SkillStat
{ {
StatObject mObject; ObjectVariant mObject;
int mIndex; int mIndex;
SkillStat(StatObject object, int index) SkillStat(ObjectVariant object, int index)
: mObject(std::move(object)) : mObject(std::move(object))
, mIndex(index) , mIndex(index)
{ {
@ -304,8 +271,7 @@ namespace MWLua
sol::object get(const Context& context, std::string_view prop, G getter) const sol::object get(const Context& context, std::string_view prop, G getter) const
{ {
return getValue( return getValue(
context, mObject, &SkillStat::setValue, mIndex, prop, [this, getter](const MWLua::Object* obj) { context, mObject, &SkillStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) {
const auto& ptr = obj->ptr();
return (ptr.getClass().getNpcStats(ptr).getSkill(mIndex).*getter)(); return (ptr.getClass().getNpcStats(ptr).getSkill(mIndex).*getter)();
}); });
} }
@ -321,22 +287,21 @@ namespace MWLua
sol::object getProgress(const Context& context) const sol::object getProgress(const Context& context) const
{ {
return getValue( return getValue(
context, mObject, &SkillStat::setValue, mIndex, "progress", [this](const MWLua::Object* obj) { context, mObject, &SkillStat::setValue, mIndex, "progress", [this](const MWWorld::Ptr& ptr) {
const auto& ptr = obj->ptr();
return getProgress(ptr, mIndex, ptr.getClass().getNpcStats(ptr).getSkill(mIndex)); 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 {};
return SkillStat{ std::move(object), index }; return SkillStat{ std::move(object), index };
} }
void cache(const Context& context, std::string_view prop, const sol::object& value) const 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()) if (obj->mStatsCache.empty())
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id())); context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value; 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)) || itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
{ {
Log(Debug::Warning) 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; return false;
} }
} }
@ -77,7 +77,7 @@ namespace MWLua
allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; }); allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; });
if (firstAllowed == allowedSlots.end()) if (firstAllowed == allowedSlots.end())
{ {
Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr); Log(Debug::Warning) << "No suitable slot for " << itemPtr.toString();
return false; return false;
} }
slot = *firstAllowed; slot = *firstAllowed;
@ -119,8 +119,6 @@ namespace MWLua
}; };
} }
using SelfObject = LocalScripts::SelfObject;
void addActorBindings(sol::table actor, const Context& context) void addActorBindings(sol::table actor, const Context& context)
{ {
actor["STANCE"] actor["STANCE"]
@ -261,7 +259,7 @@ namespace MWLua
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr())) if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
{ {
if (!equipment.empty()) 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; return;
} }
SetEquipmentAction::Equipment eqp; SetEquipmentAction::Equipment eqp;

View file

@ -95,7 +95,7 @@ namespace MWLua
std::string msg = "Requires type '"; std::string msg = "Requires type '";
msg.append(getLuaObjectTypeName(recordType)); msg.append(getLuaObjectTypeName(recordType));
msg.append("', but applied to "); msg.append("', but applied to ");
msg.append(ptrToString(ptr)); msg.append(ptr.toString());
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
return ptr; return ptr;

View file

@ -4,6 +4,8 @@
#include <components/esm3/esmwriter.hpp> #include <components/esm3/esmwriter.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwclass/container.hpp" #include "../mwclass/container.hpp"
@ -39,7 +41,7 @@ namespace MWLua
{ {
// It is important to check `isMarker` first. // It is important to check `isMarker` first.
// For example "prisonmarker" has class "Door" despite that it is only an invisible marker. // 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; return nullptr;
const MWWorld::Class& cls = ptr.getClass(); const MWWorld::Class& cls = ptr.getClass();
if (cls.isActivator()) 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) : 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. /// @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 #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) void CellRef::load(ESMReader& esm, bool& isDeleted, bool wideRefNum)
{ {
loadId(esm, wideRefNum); loadId(esm, wideRefNum);

View file

@ -27,6 +27,8 @@ namespace ESM
inline bool hasContentFile() const { return mContentFile >= 0; } inline bool hasContentFile() const { return mContentFile >= 0; }
inline bool isSet() const { return mIndex != 0 || mContentFile != -1; } inline bool isSet() const { return mIndex != 0 || mContentFile != -1; }
std::string toString() const;
}; };
/* Cell reference. This represents ONE object (of many) inside the /* Cell reference. This represents ONE object (of many) inside the