diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0b8ed449f9..27b0a9516d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -61,8 +61,8 @@ add_openmw_dir (mwscript add_openmw_dir (mwlua luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings - camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings - types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player 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/levelledlist types/terminal types/itemstats + camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings itemdata + types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player 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/levelledlist types/terminal worker magicbindings factionbindings ) diff --git a/apps/openmw/mwlua/itemdata.cpp b/apps/openmw/mwlua/itemdata.cpp new file mode 100644 index 0000000000..33a1a21310 --- /dev/null +++ b/apps/openmw/mwlua/itemdata.cpp @@ -0,0 +1,129 @@ +#include "itemdata.hpp" + +#include "context.hpp" + +#include "luamanagerimp.hpp" + +#include "../mwworld/class.hpp" + +#include "objectvariant.hpp" + +namespace +{ + using SelfObject = MWLua::SelfObject; + using Index = const SelfObject::CachedStat::Index&; + + constexpr std::array properties = { "condition", /*"enchantmentCharge", "soul", "owner", etc..*/ }; + + void invalidPropErr(std::string_view prop, const MWWorld::Ptr& ptr) + { + throw std::runtime_error(std::string(prop) + " does not exist for item " + + std::string(ptr.getClass().getName(ptr)) + "(" + std::string(ptr.getTypeDescription()) + ")"); + } +} + +namespace MWLua +{ + static void addStatUpdateAction(MWLua::LuaManager* manager, const SelfObject& obj) + { + if (!obj.mStatsCache.empty()) + return; // was already added before + manager->addAction( + [obj = Object(obj)] { + LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); + if (scripts) + scripts->applyStatsCache(); + }, + "StatUpdateAction"); + } + + class ItemData + { + ObjectVariant mObject; + + public: + ItemData(ObjectVariant object) + : mObject(object) + { + } + + sol::object get(const Context& context, std::string_view prop) const + { + if (mObject.isSelfObject()) + { + SelfObject* self = mObject.asSelfObject(); + auto it = self->mStatsCache.find({ &ItemData::setValue, std::monostate{}, prop }); + if (it != self->mStatsCache.end()) + return it->second; + } + return sol::make_object(context.mLua->sol(), getValue(context, prop)); + } + + void set(const Context& context, std::string_view prop, const sol::object& value) const + { + SelfObject* obj = mObject.asSelfObject(); + addStatUpdateAction(context.mLuaManager, *obj); + obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] = value; + } + + sol::object getValue(const Context& context, std::string_view prop) const + { + if (prop == "condition") + { + MWWorld::Ptr o = mObject.ptr(); + if (o.getClass().isLight(o)) + return sol::make_object(context.mLua->sol(), o.getClass().getRemainingUsageTime(o)); + else if (o.getClass().hasItemHealth(o)) + return sol::make_object(context.mLua->sol(), o.getClass().getItemHealth(o)); + } + + return sol::lua_nil; + } + + static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + if (prop == "condition") + { + double cond = value.as(); + if (ptr.getClass().isLight(ptr)) + ptr.getClass().setRemainingUsageTime(ptr, cond); + else if (ptr.getClass().hasItemHealth(ptr)) + ptr.getCellRef().setCharge(std::max(0, static_cast(cond))); + else /*ignore or error?*/ + invalidPropErr(prop, ptr); + } + } + }; +} + +namespace sol +{ + template <> + struct is_automagical : std::false_type + { + }; +} + +namespace MWLua +{ + void addItemDataBindings(sol::table& item, const Context& context) + { + item["itemData"] = [](const sol::object& object) -> sol::optional { + ObjectVariant o(object); + if (o.ptr().getClass().isItem(o.ptr()) || o.ptr().mRef->getType() == ESM::REC_LIGH) + return ItemData(std::move(o)); + return {}; + }; + + sol::usertype itemData = context.mLua->sol().new_usertype("ItemData"); + itemData[sol::meta_function::new_index] = [](const ItemData& stat, const sol::variadic_args args) { + throw std::runtime_error("Unknown ItemData property '" + args.get() + "'"); + }; + + for (std::string_view prop : properties) + { + itemData[prop] = sol::property([context, prop](const ItemData& stat) { return stat.get(context, prop); }, + [context, prop](const ItemData& stat, const sol::object& value) { stat.set(context, prop, value); }); + } + } +} diff --git a/apps/openmw/mwlua/itemdata.hpp b/apps/openmw/mwlua/itemdata.hpp new file mode 100644 index 0000000000..f70705fb6c --- /dev/null +++ b/apps/openmw/mwlua/itemdata.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_ITEMDATA_H +#define MWLUA_ITEMDATA_H + +#include + +namespace MWLua +{ + struct Context; + + void addItemDataBindings(sol::table& item, const Context& context); + +} +#endif // MWLUA_ITEMDATA_H diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index 6b1555868d..b87b628a89 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -22,7 +22,7 @@ namespace MWLua class CachedStat { public: - using Index = std::variant; + using Index = std::variant; using Setter = void (*)(const Index&, std::string_view, const MWWorld::Ptr&, const sol::object&); CachedStat(Setter setter, Index index, std::string_view prop) diff --git a/apps/openmw/mwlua/types/item.cpp b/apps/openmw/mwlua/types/item.cpp index 789891e8a9..b616075496 100644 --- a/apps/openmw/mwlua/types/item.cpp +++ b/apps/openmw/mwlua/types/item.cpp @@ -1,31 +1,22 @@ -#include "itemstats.hpp" +#include -namespace sol -{ - template <> - struct is_automagical : std::false_type - { - }; -} +#include "../../mwworld/class.hpp" + +#include "../itemdata.hpp" + +#include "types.hpp" namespace MWLua { - void addItemBindings(sol::table item, const Context& context) { - sol::usertype ItemStats = context.mLua->sol().new_usertype("ItemStat"); - ItemStats[sol::meta_function::new_index] = [](const ItemStat& i, const sol::variadic_args args) { - throw std::runtime_error("Unknown itemStat property '" + args.get() + "'"); - }; - ItemStats["condition"] = sol::property([](const ItemStat& i) { return i.getCondition(); }, - [](const ItemStat& i, float cond) { i.setCondition(cond); }); - item["getEnchantmentCharge"] = [](const Object& object) { return object.ptr().getCellRef().getEnchantmentCharge(); }; item["setEnchantmentCharge"] = [](const GObject& object, float charge) { object.ptr().getCellRef().setEnchantmentCharge(charge); }; item["isRestocking"] = [](const Object& object) -> bool { return object.ptr().getRefData().getCount(false) < 0; }; - item["itemStats"] = [](const sol::object& object) { return ItemStat(object); }; + + addItemDataBindings(item, context); } } diff --git a/apps/openmw/mwlua/types/itemstats.cpp b/apps/openmw/mwlua/types/itemstats.cpp deleted file mode 100644 index 4739475f6d..0000000000 --- a/apps/openmw/mwlua/types/itemstats.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "itemstats.hpp" - -namespace MWLua -{ - ItemStat::ItemStat(const sol::object& object) - : mObject(ObjectVariant(object)) - { - } - sol::optional ItemStat::getCondition() const - { - MWWorld::Ptr o = mObject.ptr(); - if (o.getClass().isLight(o)) - return o.getClass().getRemainingUsageTime(o); - else if (o.getClass().hasItemHealth(o)) - return o.getClass().getItemHealth(o); - else - return sol::nullopt; - } - void ItemStat::setCondition(float cond) const - { - if (!mObject.isGObject()) - throw std::runtime_error("This property can only be set in global scripts"); - - MWWorld::Ptr o = mObject.ptr(); - if (o.getClass().isLight(o)) - return o.getClass().setRemainingUsageTime(o, cond); - else if (o.getClass().hasItemHealth(o)) - o.getCellRef().setCharge(std::max(0, static_cast(cond))); - else - throw std::runtime_error("'condition' property does not exist for " + std::string(o.getClass().getName(o)) - + "(" + std::string(o.getTypeDescription()) + ")"); - }; -} diff --git a/apps/openmw/mwlua/types/itemstats.hpp b/apps/openmw/mwlua/types/itemstats.hpp deleted file mode 100644 index 55d55ed234..0000000000 --- a/apps/openmw/mwlua/types/itemstats.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MWLUA_ITEMSTATS_H -#define MWLUA_ITEMSTATS_H - -#include - -#include "../../mwworld/class.hpp" - -#include "../objectvariant.hpp" -#include "types.hpp" - -namespace MWLua -{ - class ItemStat - { - public: - ItemStat(const sol::object& object); - - sol::optional getCondition() const; - - void setCondition(float cond) const; - - /* - * set,get, enchantmentCharge, soul? etc.. - */ - - ObjectVariant mObject; - }; -} -#endif // MWLUA_ITEMSTATS_H