diff --git a/apps/openmw/mwlua/itemdata.cpp b/apps/openmw/mwlua/itemdata.cpp index 75848e477b..436dac24d1 100644 --- a/apps/openmw/mwlua/itemdata.cpp +++ b/apps/openmw/mwlua/itemdata.cpp @@ -13,159 +13,144 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" -namespace -{ - using SelfObject = MWLua::SelfObject; - using Index = const SelfObject::CachedStat::Index&; - - constexpr std::array properties = { "condition", "enchantmentCharge", "soul" }; - - void valueErr(std::string_view prop, std::string type) - { - throw std::logic_error("'" + std::string(prop) + "'" + " received invalid value type (" + type + ")"); - } -} - namespace MWLua { - static void addStatUpdateAction(MWLua::LuaManager* manager, const SelfObject& obj) + namespace { - 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"); - } + using Index = const SelfObject::CachedStat::Index&; - class ItemData - { - ObjectVariant mObject; + constexpr std::array properties = { "condition", "enchantmentCharge", "soul" }; - public: - ItemData(const ObjectVariant& object) - : mObject(object) + void valueErr(std::string_view prop, std::string type) { + throw std::logic_error("'" + std::string(prop) + "'" + " received invalid value type (" + type + ")"); } - sol::object get(const Context& context, std::string_view prop) const + class ItemData { - if (mObject.isSelfObject()) + ObjectVariant mObject; + + public: + ItemData(const ObjectVariant& object) + : mObject(object) { - 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->unsafeState(), getValue(context, prop, mObject.ptr())); - } - - void set(const Context& context, std::string_view prop, const sol::object& value) const - { - if (mObject.isGObject()) - setValue({}, prop, mObject.ptr(), value); - else if (mObject.isSelfObject()) - { - SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] - = sol::main_object(value); - } - else - throw std::runtime_error("Only global or self scripts can set the value"); - } - - static sol::object getValue(const Context& context, std::string_view prop, const MWWorld::Ptr& ptr) - { - if (prop == "condition") - { - if (ptr.mRef->getType() == ESM::REC_LIGH) - return sol::make_object(context.mLua->unsafeState(), ptr.getClass().getRemainingUsageTime(ptr)); - else if (ptr.getClass().hasItemHealth(ptr)) - return sol::make_object(context.mLua->unsafeState(), - ptr.getClass().getItemHealth(ptr) + ptr.getCellRef().getChargeIntRemainder()); - } - else if (prop == "enchantmentCharge") - { - const ESM::RefId& enchantmentName = ptr.getClass().getEnchantment(ptr); - - if (enchantmentName.empty()) - return sol::lua_nil; - - float charge = ptr.getCellRef().getEnchantmentCharge(); - const auto& store = MWBase::Environment::get().getESMStore(); - const auto* enchantment = store->get().find(enchantmentName); - - if (charge == -1) // return the full charge - return sol::make_object( - context.mLua->unsafeState(), MWMechanics::getEnchantmentCharge(*enchantment)); - - return sol::make_object(context.mLua->unsafeState(), charge); - } - else if (prop == "soul") - { - ESM::RefId soul = ptr.getCellRef().getSoul(); - if (soul.empty()) - return sol::lua_nil; - - return sol::make_object(context.mLua->unsafeState(), soul.serializeText()); } - return sol::lua_nil; - } - - static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) - { - if (prop == "condition") + sol::object get(const Context& context, std::string_view prop) const { - if (value.get_type() == sol::type::number) + if (mObject.isSelfObject()) + { + SelfObject* self = mObject.asSelfObject(); + if (auto value = self->getCachedStat({ &ItemData::setValue, std::monostate{}, prop })) + return *value; + } + return sol::make_object(context.mLua->unsafeState(), getValue(context, prop, mObject.ptr())); + } + + void set(const Context& context, std::string_view prop, const sol::object& value) const + { + if (mObject.isGObject()) + setValue({}, prop, mObject.ptr(), value); + else if (mObject.isSelfObject()) + { + SelfObject* obj = mObject.asSelfObject(); + obj->cacheStat(*context.mLuaManager, + SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }, value); + } + else + throw std::runtime_error("Only global or self scripts can set the value"); + } + + static sol::object getValue(const Context& context, std::string_view prop, const MWWorld::Ptr& ptr) + { + if (prop == "condition") { - float cond = LuaUtil::cast(value); if (ptr.mRef->getType() == ESM::REC_LIGH) - ptr.getClass().setRemainingUsageTime(ptr, cond); + return sol::make_object(context.mLua->unsafeState(), ptr.getClass().getRemainingUsageTime(ptr)); else if (ptr.getClass().hasItemHealth(ptr)) - { - // 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, static_cast(cond))); - } + return sol::make_object(context.mLua->unsafeState(), + ptr.getClass().getItemHealth(ptr) + ptr.getCellRef().getChargeIntRemainder()); } - else - valueErr(prop, sol::type_name(value.lua_state(), value.get_type())); - } - else if (prop == "enchantmentCharge") - { - if (value.get_type() == sol::type::lua_nil) - ptr.getCellRef().setEnchantmentCharge(-1); - else if (value.get_type() == sol::type::number) - ptr.getCellRef().setEnchantmentCharge(std::max(0.0f, LuaUtil::cast(value))); - else - valueErr(prop, sol::type_name(value.lua_state(), value.get_type())); - } - else if (prop == "soul") - { - if (value.get_type() == sol::type::lua_nil) - ptr.getCellRef().setSoul(ESM::RefId{}); - else if (value.get_type() == sol::type::string) + else if (prop == "enchantmentCharge") { - std::string_view souldId = LuaUtil::cast(value); - ESM::RefId creature = ESM::RefId::deserializeText(souldId); - const auto& store = *MWBase::Environment::get().getESMStore(); + const ESM::RefId& enchantmentName = ptr.getClass().getEnchantment(ptr); - // TODO: Add Support for NPC Souls - if (store.get().search(creature)) - ptr.getCellRef().setSoul(creature); - else - throw std::runtime_error("Cannot use non-existent creature as a soul: " + std::string(souldId)); + if (enchantmentName.empty()) + return sol::lua_nil; + + float charge = ptr.getCellRef().getEnchantmentCharge(); + const auto& store = MWBase::Environment::get().getESMStore(); + const auto* enchantment = store->get().find(enchantmentName); + + if (charge == -1) // return the full charge + return sol::make_object( + context.mLua->unsafeState(), MWMechanics::getEnchantmentCharge(*enchantment)); + + return sol::make_object(context.mLua->unsafeState(), charge); } - else - valueErr(prop, sol::type_name(value.lua_state(), value.get_type())); + else if (prop == "soul") + { + ESM::RefId soul = ptr.getCellRef().getSoul(); + if (soul.empty()) + return sol::lua_nil; + + return sol::make_object(context.mLua->unsafeState(), soul.serializeText()); + } + + return sol::lua_nil; } - } - }; + + static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + if (prop == "condition") + { + if (value.get_type() == sol::type::number) + { + float cond = LuaUtil::cast(value); + if (ptr.mRef->getType() == ESM::REC_LIGH) + ptr.getClass().setRemainingUsageTime(ptr, cond); + else if (ptr.getClass().hasItemHealth(ptr)) + { + // 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, static_cast(cond))); + } + } + else + valueErr(prop, sol::type_name(value.lua_state(), value.get_type())); + } + else if (prop == "enchantmentCharge") + { + if (value.get_type() == sol::type::lua_nil) + ptr.getCellRef().setEnchantmentCharge(-1); + else if (value.get_type() == sol::type::number) + ptr.getCellRef().setEnchantmentCharge(std::max(0.0f, LuaUtil::cast(value))); + else + valueErr(prop, sol::type_name(value.lua_state(), value.get_type())); + } + else if (prop == "soul") + { + if (value.get_type() == sol::type::lua_nil) + ptr.getCellRef().setSoul(ESM::RefId{}); + else if (value.get_type() == sol::type::string) + { + std::string_view souldId = LuaUtil::cast(value); + ESM::RefId creature = ESM::RefId::deserializeText(souldId); + const auto& store = *MWBase::Environment::get().getESMStore(); + + // TODO: Add Support for NPC Souls + if (store.get().search(creature)) + ptr.getCellRef().setSoul(creature); + else + throw std::runtime_error( + "Cannot use non-existent creature as a soul: " + std::string(souldId)); + } + else + valueErr(prop, sol::type_name(value.lua_state(), value.get_type())); + } + } + }; + } } namespace sol diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index d9834abd91..38f45535ee 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -16,6 +16,7 @@ #include "../mwworld/ptr.hpp" #include "context.hpp" +#include "luamanagerimp.hpp" namespace sol { @@ -31,6 +32,20 @@ namespace sol namespace MWLua { + void SelfObject::cacheStat(LuaManager& manager, SelfObject::CachedStat key, sol::main_object value) + { + if (mStatsCache.empty()) + { + manager.addAction( + [obj = Object(*this)] { + LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); + if (scripts) + scripts->applyStatsCache(); + }, + "StatUpdateAction"); + } + mStatsCache[std::move(key)] = std::move(value); + } void LocalScripts::initializeSelfPackage(const Context& context) { diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index c51d2d8c19..9e8c80c3a2 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -15,6 +15,8 @@ namespace MWLua { struct Context; + class LocalScripts; + class LuaManager; struct SelfObject : public LObject { @@ -52,9 +54,22 @@ namespace MWLua , mIsActive(false) { } + + const sol::main_object* getCachedStat(const CachedStat& key) const + { + auto it = mStatsCache.find(key); + if (it != mStatsCache.end()) + return &it->second; + return nullptr; + } + void cacheStat(LuaManager&, CachedStat, sol::main_object); + MWBase::LuaManager::ActorControls mControls; - std::map mStatsCache; bool mIsActive; + + private: + friend class LocalScripts; + std::map mStatsCache; }; class LocalScripts : public LuaUtil::ScriptsContainer diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 93cd8c294e..53de318bd0 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -52,9 +52,8 @@ namespace if (obj.isSelfObject()) { SelfObject* self = obj.asSelfObject(); - auto it = self->mStatsCache.find({ setter, index, prop }); - if (it != self->mStatsCache.end()) - return it->second; + if (auto value = self->getCachedStat({ setter, index, prop })) + return *value; } return sol::make_object(context.mLua->unsafeState(), getter(obj.ptr())); } @@ -64,19 +63,6 @@ namespace MWLua { namespace { - 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"); - } - static void setCreatureValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) { auto& stats = ptr.getClass().getCreatureStats(ptr); @@ -125,9 +111,8 @@ namespace MWLua return; SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }] - = sol::main_object(value); + obj->cacheStat(*context.mLuaManager, + SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }, value); } }; @@ -160,10 +145,8 @@ namespace MWLua return; SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ - &setNpcValue, specialization, "skillIncreasesForSpecialization" }] - = sol::main_object(value); + obj->cacheStat(*context.mLuaManager, + SelfObject::CachedStat{ &setNpcValue, specialization, "skillIncreasesForSpecialization" }, value); } }; @@ -186,9 +169,8 @@ namespace MWLua void setCurrent(const Context& context, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }] - = sol::main_object(value); + obj->cacheStat(*context.mLuaManager, + SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }, value); } sol::object getProgress(const Context& context) const @@ -207,9 +189,8 @@ namespace MWLua return; SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }] - = sol::main_object(value); + obj->cacheStat( + *context.mLuaManager, SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }, value); } SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const @@ -262,9 +243,8 @@ namespace MWLua void cache(const Context& context, std::string_view prop, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] - = sol::main_object(value); + obj->cacheStat( + *context.mLuaManager, SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }, value); } static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) @@ -324,9 +304,8 @@ namespace MWLua void cache(const Context& context, std::string_view prop, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }] - = sol::main_object(value); + obj->cacheStat( + *context.mLuaManager, SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }, value); } static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) @@ -411,8 +390,7 @@ namespace MWLua void cache(const Context& context, std::string_view prop, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = sol::main_object(value); + obj->cacheStat(*context.mLuaManager, SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }, value); } static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) @@ -474,9 +452,8 @@ namespace MWLua void cache(const Context& context, std::string_view prop, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &AIStat::setValue, static_cast(mIndex), prop }] - = sol::main_object(value); + obj->cacheStat(*context.mLuaManager, + SelfObject::CachedStat{ &AIStat::setValue, static_cast(mIndex), prop }, value); } static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)