From ff16ee2d64be745378acae3ae728f821e195fcda Mon Sep 17 00:00:00 2001 From: Kindi Date: Tue, 12 Sep 2023 16:29:00 +0800 Subject: [PATCH 1/5] implement lua api for get/set item condition --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/light.cpp | 5 ++++ apps/openmw/mwclass/light.hpp | 2 ++ apps/openmw/mwlua/types/item.cpp | 23 +++++++++++++++---- apps/openmw/mwlua/types/itemstats.cpp | 33 +++++++++++++++++++++++++++ apps/openmw/mwlua/types/itemstats.hpp | 29 +++++++++++++++++++++++ apps/openmw/mwlua/types/types.cpp | 8 ++++--- apps/openmw/mwlua/types/types.hpp | 2 +- apps/openmw/mwworld/class.hpp | 2 ++ 9 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 apps/openmw/mwlua/types/itemstats.cpp create mode 100644 apps/openmw/mwlua/types/itemstats.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 96d54949b1a..0b8ed449f9d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -62,7 +62,7 @@ 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/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 worker magicbindings factionbindings ) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 931ed73dfe7..8933491e683 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -91,6 +91,11 @@ namespace MWClass return ptr.get()->mBase->mData.mFlags & ESM::Light::Carry; } + bool Light::isLight(const MWWorld::ConstPtr& ptr) const + { + return true; + } + std::unique_ptr Light::activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 70d6852ff83..d5119aa0b59 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -38,6 +38,8 @@ namespace MWClass bool isItem(const MWWorld::ConstPtr&) const override; + bool isLight(const MWWorld::ConstPtr&) const override; + std::unique_ptr activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; ///< Generate action for activation diff --git a/apps/openmw/mwlua/types/item.cpp b/apps/openmw/mwlua/types/item.cpp index e15be1a2e4c..789891e8a99 100644 --- a/apps/openmw/mwlua/types/item.cpp +++ b/apps/openmw/mwlua/types/item.cpp @@ -1,18 +1,31 @@ -#include +#include "itemstats.hpp" -#include "../../mwworld/class.hpp" - -#include "types.hpp" +namespace sol +{ + template <> + struct is_automagical : std::false_type + { + }; +} namespace MWLua { - void addItemBindings(sol::table item) + + 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); }; } } diff --git a/apps/openmw/mwlua/types/itemstats.cpp b/apps/openmw/mwlua/types/itemstats.cpp new file mode 100644 index 00000000000..4739475f6df --- /dev/null +++ b/apps/openmw/mwlua/types/itemstats.cpp @@ -0,0 +1,33 @@ +#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 new file mode 100644 index 00000000000..55d55ed234c --- /dev/null +++ b/apps/openmw/mwlua/types/itemstats.hpp @@ -0,0 +1,29 @@ +#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 diff --git a/apps/openmw/mwlua/types/types.cpp b/apps/openmw/mwlua/types/types.cpp index 6095053eeee..b3e39b02643 100644 --- a/apps/openmw/mwlua/types/types.cpp +++ b/apps/openmw/mwlua/types/types.cpp @@ -185,9 +185,11 @@ namespace MWLua addActorBindings( addType(ObjectTypeName::Actor, { ESM::REC_INTERNAL_PLAYER, ESM::REC_CREA, ESM::REC_NPC_ }), context); - addItemBindings(addType(ObjectTypeName::Item, - { ESM::REC_ARMO, ESM::REC_BOOK, ESM::REC_CLOT, ESM::REC_INGR, ESM::REC_LIGH, ESM::REC_MISC, ESM::REC_ALCH, - ESM::REC_WEAP, ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA })); + addItemBindings( + addType(ObjectTypeName::Item, + { ESM::REC_ARMO, ESM::REC_BOOK, ESM::REC_CLOT, ESM::REC_INGR, ESM::REC_LIGH, ESM::REC_MISC, + ESM::REC_ALCH, ESM::REC_WEAP, ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA }), + context); addLockableBindings( addType(ObjectTypeName::Lockable, { ESM::REC_CONT, ESM::REC_DOOR, ESM::REC_CONT4, ESM::REC_DOOR4 })); diff --git a/apps/openmw/mwlua/types/types.hpp b/apps/openmw/mwlua/types/types.hpp index 8fc0932dfeb..adac3722775 100644 --- a/apps/openmw/mwlua/types/types.hpp +++ b/apps/openmw/mwlua/types/types.hpp @@ -47,7 +47,7 @@ namespace MWLua void addBookBindings(sol::table book, const Context& context); void addContainerBindings(sol::table container, const Context& context); void addDoorBindings(sol::table door, const Context& context); - void addItemBindings(sol::table item); + void addItemBindings(sol::table item, const Context& context); void addActorBindings(sol::table actor, const Context& context); void addWeaponBindings(sol::table weapon, const Context& context); void addNpcBindings(sol::table npc, const Context& context); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 7b7e9135baa..162fa88a341 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -332,6 +332,8 @@ namespace MWWorld // True if it is an item that can be picked up. virtual bool isItem(const MWWorld::ConstPtr& ptr) const { return false; } + virtual bool isLight(const MWWorld::ConstPtr& ptr) const { return false; } + virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const; virtual bool canFly(const MWWorld::ConstPtr& ptr) const; virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; From 83ebaf27cc4552d35c597598920eae72202319ad Mon Sep 17 00:00:00 2001 From: Kindi Date: Fri, 15 Sep 2023 23:26:58 +0800 Subject: [PATCH 2/5] take2 --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/mwlua/itemdata.cpp | 129 ++++++++++++++++++++++++++ apps/openmw/mwlua/itemdata.hpp | 13 +++ apps/openmw/mwlua/localscripts.hpp | 2 +- apps/openmw/mwlua/types/item.cpp | 25 ++--- apps/openmw/mwlua/types/itemstats.cpp | 33 ------- apps/openmw/mwlua/types/itemstats.hpp | 29 ------ 7 files changed, 153 insertions(+), 82 deletions(-) create mode 100644 apps/openmw/mwlua/itemdata.cpp create mode 100644 apps/openmw/mwlua/itemdata.hpp delete mode 100644 apps/openmw/mwlua/types/itemstats.cpp delete mode 100644 apps/openmw/mwlua/types/itemstats.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0b8ed449f9d..27b0a9516dc 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 00000000000..33a1a213107 --- /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 00000000000..f70705fb6c6 --- /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 6b1555868d4..b87b628a893 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 789891e8a99..b6160754965 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 4739475f6df..00000000000 --- 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 55d55ed234c..00000000000 --- 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 From 58a16dacbe794574d00117f4d768af0b0b4817f3 Mon Sep 17 00:00:00 2001 From: Kindi Date: Fri, 13 Oct 2023 00:53:59 +0800 Subject: [PATCH 3/5] take2 resolve --- apps/openmw/mwclass/light.cpp | 5 ----- apps/openmw/mwclass/light.hpp | 2 -- apps/openmw/mwlua/itemdata.cpp | 25 +++++++++++++++++++------ apps/openmw/mwlua/stats.cpp | 4 ++-- apps/openmw/mwworld/cellref.cpp | 27 ++++++++++++++++----------- apps/openmw/mwworld/cellref.hpp | 13 +++++++++++++ apps/openmw/mwworld/class.hpp | 2 -- components/esm3/cellref.hpp | 2 +- 8 files changed, 51 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 8933491e683..931ed73dfe7 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -91,11 +91,6 @@ namespace MWClass return ptr.get()->mBase->mData.mFlags & ESM::Light::Carry; } - bool Light::isLight(const MWWorld::ConstPtr& ptr) const - { - return true; - } - std::unique_ptr Light::activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index d5119aa0b59..70d6852ff83 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -38,8 +38,6 @@ namespace MWClass bool isItem(const MWWorld::ConstPtr&) const override; - bool isLight(const MWWorld::ConstPtr&) const override; - std::unique_ptr activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; ///< Generate action for activation diff --git a/apps/openmw/mwlua/itemdata.cpp b/apps/openmw/mwlua/itemdata.cpp index 33a1a213107..0741b5ebe2b 100644 --- a/apps/openmw/mwlua/itemdata.cpp +++ b/apps/openmw/mwlua/itemdata.cpp @@ -71,10 +71,11 @@ namespace MWLua if (prop == "condition") { MWWorld::Ptr o = mObject.ptr(); - if (o.getClass().isLight(o)) + if (o.mRef->getType() == ESM::REC_LIGH) 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::make_object( + context.mLua->sol(), o.getClass().getItemHealth(o) + o.getCellRef().getChargeIntRemainder()); } return sol::lua_nil; @@ -84,14 +85,26 @@ namespace MWLua { if (prop == "condition") { - double cond = value.as(); - if (ptr.getClass().isLight(ptr)) + float cond = LuaUtil::cast(value); + if (ptr.mRef->getType() == ESM::REC_LIGH) ptr.getClass().setRemainingUsageTime(ptr, cond); else if (ptr.getClass().hasItemHealth(ptr)) - ptr.getCellRef().setCharge(std::max(0, static_cast(cond))); - else /*ignore or error?*/ + { + const float lastChargeRemainder = ptr.getCellRef().getChargeIntRemainder(); + // if the value set is less than 0, chargeInt and chargeIntRemainder is set to 0 + ptr.getCellRef().setChargeIntRemainder(std::max(0.f, std::modf(cond, &cond))); + ptr.getCellRef().setCharge(std::max(0.f, cond)); + // resolve the remaining charge remainder to be subtracted + if (lastChargeRemainder < 0) + ptr.getCellRef().applyChargeRemainderToBeSubtracted(lastChargeRemainder); + } + else invalidPropErr(prop, ptr); + return; } + + /*ignore or error?*/ + invalidPropErr(prop, ptr); } }; } diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 5c1e536dd6f..35273a64e8d 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -85,7 +85,7 @@ namespace MWLua public: sol::object getCurrent(const Context& context) const { - return getValue(context, mObject, &LevelStat::setValue, 0, "current", + return getValue(context, mObject, &LevelStat::setValue, std::monostate{}, "current", [](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); }); } @@ -93,7 +93,7 @@ namespace MWLua { SelfObject* obj = mObject.asSelfObject(); addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value; + obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, std::monostate{}, "current" }] = value; } sol::object getProgress(const Context& context) const diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 37e1be0ddf3..73fd3d4ac86 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -188,19 +188,14 @@ namespace MWWorld void CellRef::applyChargeRemainderToBeSubtracted(float chargeRemainder) { auto esm3Visit = [&](ESM::CellRef& cellRef3) { - cellRef3.mChargeIntRemainder += std::abs(chargeRemainder); - if (cellRef3.mChargeIntRemainder > 1.0f) + cellRef3.mChargeIntRemainder -= std::abs(chargeRemainder); + if (cellRef3.mChargeIntRemainder <= -1.0f) { - float newChargeRemainder = (cellRef3.mChargeIntRemainder - std::floor(cellRef3.mChargeIntRemainder)); - if (cellRef3.mChargeInt <= static_cast(cellRef3.mChargeIntRemainder)) - { - cellRef3.mChargeInt = 0; - } - else - { - cellRef3.mChargeInt -= static_cast(cellRef3.mChargeIntRemainder); - } + float newChargeRemainder = std::modf(cellRef3.mChargeIntRemainder, &cellRef3.mChargeIntRemainder); + cellRef3.mChargeInt += static_cast(cellRef3.mChargeIntRemainder); cellRef3.mChargeIntRemainder = newChargeRemainder; + if (cellRef3.mChargeInt < 0) + cellRef3.mChargeInt = 0; } }; std::visit(ESM::VisitOverload{ @@ -211,6 +206,16 @@ namespace MWWorld mCellRef.mVariant); } + void CellRef::setChargeIntRemainder(float chargeRemainder) + { + std::visit(ESM::VisitOverload{ + [&](ESM4::Reference& /*ref*/) {}, + [&](ESM4::ActorCharacter&) {}, + [&](ESM::CellRef& ref) { ref.mChargeIntRemainder = chargeRemainder; }, + }, + mCellRef.mVariant); + } + void CellRef::setChargeFloat(float charge) { std::visit(ESM::VisitOverload{ diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 73e721278ef..a1ddd8d0f00 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -118,10 +118,23 @@ namespace MWWorld }; return std::visit(Visitor(), mCellRef.mVariant); } // Implemented as union with int charge + float getChargeIntRemainder() const + { + struct Visitor + { + float operator()(const ESM::CellRef& ref) { return ref.mChargeIntRemainder; } + float operator()(const ESM4::Reference& /*ref*/) { return 0; } + float operator()(const ESM4::ActorCharacter&) { throw std::logic_error("Not applicable"); } + }; + return std::visit(Visitor(), mCellRef.mVariant); + } void setCharge(int charge); void setChargeFloat(float charge); void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1 + // Stores fractional part of mChargeInt + void setChargeIntRemainder(float chargeRemainder); + // The NPC that owns this object (and will get angry if you steal it) ESM::RefId getOwner() const { diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 162fa88a341..7b7e9135baa 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -332,8 +332,6 @@ namespace MWWorld // True if it is an item that can be picked up. virtual bool isItem(const MWWorld::ConstPtr& ptr) const { return false; } - virtual bool isLight(const MWWorld::ConstPtr& ptr) const { return false; } - virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const; virtual bool canFly(const MWWorld::ConstPtr& ptr) const; virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; diff --git a/components/esm3/cellref.hpp b/components/esm3/cellref.hpp index 10ab4505cec..55e5afcbf58 100644 --- a/components/esm3/cellref.hpp +++ b/components/esm3/cellref.hpp @@ -60,7 +60,7 @@ namespace ESM int32_t mChargeInt; // Used by everything except lights float mChargeFloat; // Used only by lights }; - float mChargeIntRemainder; // Stores amount of charge not subtracted from mChargeInt + float mChargeIntRemainder; // Fractional part of mChargeInt // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float mEnchantmentCharge; From 7e087707cd4ac4d998117149135a348fccacb3ac Mon Sep 17 00:00:00 2001 From: Kindi Date: Sun, 15 Oct 2023 21:26:47 +0800 Subject: [PATCH 4/5] fix potential miscalculation --- apps/openmw/mwlua/itemdata.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwlua/itemdata.cpp b/apps/openmw/mwlua/itemdata.cpp index 0741b5ebe2b..f2bd32703f7 100644 --- a/apps/openmw/mwlua/itemdata.cpp +++ b/apps/openmw/mwlua/itemdata.cpp @@ -90,13 +90,9 @@ namespace MWLua ptr.getClass().setRemainingUsageTime(ptr, cond); else if (ptr.getClass().hasItemHealth(ptr)) { - const float lastChargeRemainder = ptr.getCellRef().getChargeIntRemainder(); // if the value set is less than 0, chargeInt and chargeIntRemainder is set to 0 ptr.getCellRef().setChargeIntRemainder(std::max(0.f, std::modf(cond, &cond))); ptr.getCellRef().setCharge(std::max(0.f, cond)); - // resolve the remaining charge remainder to be subtracted - if (lastChargeRemainder < 0) - ptr.getCellRef().applyChargeRemainderToBeSubtracted(lastChargeRemainder); } else invalidPropErr(prop, ptr); From 1bff02e3b00e615ead11c487593c03711b203795 Mon Sep 17 00:00:00 2001 From: Kindi Date: Thu, 26 Oct 2023 03:32:15 +0800 Subject: [PATCH 5/5] add docs --- apps/openmw/mwlua/itemdata.cpp | 6 +----- apps/openmw/mwworld/cellref.hpp | 2 +- files/lua_api/openmw/types.lua | 9 +++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwlua/itemdata.cpp b/apps/openmw/mwlua/itemdata.cpp index f2bd32703f7..6dea2360e7c 100644 --- a/apps/openmw/mwlua/itemdata.cpp +++ b/apps/openmw/mwlua/itemdata.cpp @@ -17,7 +17,7 @@ namespace void invalidPropErr(std::string_view prop, const MWWorld::Ptr& ptr) { - throw std::runtime_error(std::string(prop) + " does not exist for item " + throw std::runtime_error("'" + std::string(prop) + "'" + " property does not exist for item " + std::string(ptr.getClass().getName(ptr)) + "(" + std::string(ptr.getTypeDescription()) + ")"); } } @@ -96,11 +96,7 @@ namespace MWLua } else invalidPropErr(prop, ptr); - return; } - - /*ignore or error?*/ - invalidPropErr(prop, ptr); } }; } diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index a1ddd8d0f00..9595a4cc4cf 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -130,7 +130,7 @@ namespace MWWorld } void setCharge(int charge); void setChargeFloat(float charge); - void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1 + void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if <= -1 // Stores fractional part of mChargeInt void setChargeIntRemainder(float chargeRemainder); diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 03efe885b57..b9a6958654b 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -658,6 +658,15 @@ -- @param openmw.core#GameObject object -- @return #boolean +--- +-- Set of properties that differentiates one item from another of the same record type. +-- @function [parent=#Item] itemData +-- @param openmw.core#GameObject item +-- @return #ItemData + +--- +-- @type ItemData +-- @field #number condition The item's current condition. Time remaining for lights. Uses left for lockpicks and probes. Current health for weapons and armor. -------------------------------------------------------------------------------- -- @{#Creature} functions