mirror of
https://github.com/OpenMW/openmw.git
synced 2025-04-26 15:06:48 +00:00
Merge branch 'ptr' into 'master'
MWLua refactoring See merge request OpenMW/openmw!2803
This commit is contained in:
commit
df89a8c845
16 changed files with 263 additions and 229 deletions
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
{
|
||||
};
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
55
apps/openmw/mwlua/objectvariant.hpp
Normal file
55
apps/openmw/mwlua/objectvariant.hpp
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
|
|
42
apps/openmw/mwworld/ptr.cpp
Normal file
42
apps/openmw/mwworld/ptr.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue