From f884c26631d5af3f3eeff9d9e7cd127730a35847 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 22 Dec 2025 17:42:50 +0000 Subject: [PATCH] Maybe an anonymous namespace reduces RAM requirements This file is the most likely to make the compiler run out of heap space --- apps/openmw/mwlua/stats.cpp | 811 ++++++++++++++++++------------------ 1 file changed, 410 insertions(+), 401 deletions(-) diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index e4ee31d411..93cd8c294e 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -62,428 +62,437 @@ namespace 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"); - } - - static void setCreatureValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) - { - auto& stats = ptr.getClass().getCreatureStats(ptr); - if (prop == "current") - stats.setLevel(LuaUtil::cast(value)); - } - - static void setNpcValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) - { - auto& stats = ptr.getClass().getNpcStats(ptr); - if (prop == "progress") - stats.setLevelProgress(LuaUtil::cast(value)); - else if (prop == "skillIncreasesForAttribute") - stats.setSkillIncreasesForAttribute( - *std::get(index).getIf(), LuaUtil::cast(value)); - else if (prop == "skillIncreasesForSpecialization") - stats.setSkillIncreasesForSpecialization( - static_cast(std::get(index)), LuaUtil::cast(value)); - } - - class SkillIncreasesForAttributeStats - { - ObjectVariant mObject; - - public: - SkillIncreasesForAttributeStats(ObjectVariant object) - : mObject(std::move(object)) + 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"); } - sol::object get(const Context& context, ESM::StringRefId attributeId) const + static void setCreatureValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) { - if (!mObject.ptr().getClass().isNpc()) - return sol::nil; - - return getValue(context, mObject, &setNpcValue, attributeId, "skillIncreasesForAttribute", - [attributeId](const MWWorld::Ptr& ptr) { - return ptr.getClass().getNpcStats(ptr).getSkillIncreasesForAttribute(attributeId); - }); - } - - void set(const Context& context, ESM::StringRefId attributeId, const sol::object& value) const - { - const auto& ptr = mObject.ptr(); - if (!ptr.getClass().isNpc()) - return; - - SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }] - = sol::main_object(value); - } - }; - - class SkillIncreasesForSpecializationStats - { - ObjectVariant mObject; - - public: - SkillIncreasesForSpecializationStats(ObjectVariant object) - : mObject(std::move(object)) - { - } - - sol::object get(const Context& context, int specialization) const - { - if (!mObject.ptr().getClass().isNpc()) - return sol::nil; - - return getValue(context, mObject, &setNpcValue, specialization, "skillIncreasesForSpecialization", - [specialization](const MWWorld::Ptr& ptr) { - return ptr.getClass().getNpcStats(ptr).getSkillIncreasesForSpecialization( - static_cast(specialization)); - }); - } - - void set(const Context& context, int specialization, const sol::object& value) const - { - const auto& ptr = mObject.ptr(); - if (!ptr.getClass().isNpc()) - return; - - SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, specialization, "skillIncreasesForSpecialization" }] - = sol::main_object(value); - } - }; - - class LevelStat - { - ObjectVariant mObject; - - LevelStat(ObjectVariant object) - : mObject(std::move(object)) - { - } - - public: - sol::object getCurrent(const Context& context) const - { - return getValue(context, mObject, &setCreatureValue, std::monostate{}, "current", - [](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); }); - } - - 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); - } - - sol::object getProgress(const Context& context) const - { - if (!mObject.ptr().getClass().isNpc()) - return sol::nil; - - return getValue(context, mObject, &setNpcValue, std::monostate{}, "progress", - [](const MWWorld::Ptr& ptr) { return ptr.getClass().getNpcStats(ptr).getLevelProgress(); }); - } - - void setProgress(const Context& context, const sol::object& value) const - { - const auto& ptr = mObject.ptr(); - if (!ptr.getClass().isNpc()) - return; - - SelfObject* obj = mObject.asSelfObject(); - addStatUpdateAction(context.mLuaManager, *obj); - obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }] - = sol::main_object(value); - } - - SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const - { - return SkillIncreasesForAttributeStats{ mObject }; - } - - SkillIncreasesForSpecializationStats getSkillIncreasesForSpecializationStats() const - { - return SkillIncreasesForSpecializationStats{ mObject }; - } - - static std::optional create(ObjectVariant object, Index) - { - if (!object.ptr().getClass().isActor()) - return {}; - return LevelStat{ std::move(object) }; - } - }; - - class DynamicStat - { - ObjectVariant mObject; - int mIndex; - - DynamicStat(ObjectVariant object, int index) - : mObject(std::move(object)) - , mIndex(index) - { - } - - public: - template - sol::object get(const Context& context, std::string_view prop, G getter) const - { - return getValue( - context, mObject, &DynamicStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) { - return (ptr.getClass().getCreatureStats(ptr).getDynamic(mIndex).*getter)(); - }); - } - - static std::optional create(ObjectVariant object, Index i) - { - if (!object.ptr().getClass().isActor()) - return {}; - int index = std::get(i); - return DynamicStat{ std::move(object), index }; - } - - 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); - } - - static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) - { - int index = std::get(i); auto& stats = ptr.getClass().getCreatureStats(ptr); - auto stat = stats.getDynamic(index); - float floatValue = LuaUtil::cast(value); - if (prop == "base") - stat.setBase(floatValue); - else if (prop == "current") - stat.setCurrent(floatValue, true, true); - else if (prop == "modifier") - stat.setModifier(floatValue); - stats.setDynamic(index, stat); - } - }; - - class AttributeStat - { - ObjectVariant mObject; - ESM::RefId mId; - - AttributeStat(ObjectVariant object, ESM::RefId id) - : mObject(std::move(object)) - , mId(id) - { + if (prop == "current") + stats.setLevel(LuaUtil::cast(value)); } - public: - template - sol::object get(const Context& context, std::string_view prop, G getter) const + static void setNpcValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) { - return getValue( - context, mObject, &AttributeStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) { - return (ptr.getClass().getCreatureStats(ptr).getAttribute(mId).*getter)(); - }); - } - - float getModified(const Context& context) const - { - auto base = LuaUtil::cast(get(context, "base", &MWMechanics::AttributeValue::getBase)); - auto damage = LuaUtil::cast(get(context, "damage", &MWMechanics::AttributeValue::getDamage)); - auto modifier = LuaUtil::cast(get(context, "modifier", &MWMechanics::AttributeValue::getModifier)); - return std::max(0.f, base - damage + modifier); // Should match AttributeValue::getModified - } - - static std::optional create(ObjectVariant object, Index i) - { - if (!object.ptr().getClass().isActor()) - return {}; - ESM::RefId id = std::get(i); - return AttributeStat{ std::move(object), id }; - } - - 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); - } - - static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) - { - ESM::RefId id = std::get(i); - auto& stats = ptr.getClass().getCreatureStats(ptr); - auto stat = stats.getAttribute(id); - float floatValue = LuaUtil::cast(value); - if (prop == "base") - stat.setBase(floatValue); - else if (prop == "damage") - { - stat.restore(stat.getDamage()); - stat.damage(floatValue); - } - else if (prop == "modifier") - stat.setModifier(floatValue); - stats.setAttribute(id, stat); - } - }; - - class SkillStat - { - ObjectVariant mObject; - ESM::RefId mId; - - SkillStat(ObjectVariant object, ESM::RefId id) - : mObject(std::move(object)) - , mId(id) - { - } - - static float getProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat) - { - float progress = stat.getProgress(); - if (progress != 0.f) - progress /= getMaxProgress(ptr, id, stat); - return progress; - } - - static float getMaxProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat) - { - const auto& store = *MWBase::Environment::get().getESMStore(); - const auto cl = store.get().find(ptr.get()->mBase->mClass); - return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(id, *cl); - } - - public: - template - sol::object get(const Context& context, std::string_view prop, G getter) const - { - return getValue(context, mObject, &SkillStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) { - return (ptr.getClass().getNpcStats(ptr).getSkill(mId).*getter)(); - }); - } - - float getModified(const Context& context) const - { - auto base = LuaUtil::cast(get(context, "base", &MWMechanics::SkillValue::getBase)); - auto damage = LuaUtil::cast(get(context, "damage", &MWMechanics::SkillValue::getDamage)); - auto modifier = LuaUtil::cast(get(context, "modifier", &MWMechanics::SkillValue::getModifier)); - return std::max(0.f, base - damage + modifier); // Should match SkillValue::getModified - } - - sol::object getProgress(const Context& context) const - { - return getValue(context, mObject, &SkillStat::setValue, mId, "progress", [this](const MWWorld::Ptr& ptr) { - return getProgress(ptr, mId, ptr.getClass().getNpcStats(ptr).getSkill(mId)); - }); - } - - static std::optional create(ObjectVariant object, Index index) - { - if (!object.ptr().getClass().isNpc()) - return {}; - ESM::RefId id = std::get(index); - return SkillStat{ std::move(object), id }; - } - - 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); - } - - static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) - { - ESM::RefId id = std::get(index); auto& stats = ptr.getClass().getNpcStats(ptr); - auto stat = stats.getSkill(id); - float floatValue = LuaUtil::cast(value); - if (prop == "base") - stat.setBase(floatValue); - else if (prop == "damage") + if (prop == "progress") + stats.setLevelProgress(LuaUtil::cast(value)); + else if (prop == "skillIncreasesForAttribute") + stats.setSkillIncreasesForAttribute( + *std::get(index).getIf(), LuaUtil::cast(value)); + else if (prop == "skillIncreasesForSpecialization") + stats.setSkillIncreasesForSpecialization( + static_cast(std::get(index)), LuaUtil::cast(value)); + } + + class SkillIncreasesForAttributeStats + { + ObjectVariant mObject; + + public: + SkillIncreasesForAttributeStats(ObjectVariant object) + : mObject(std::move(object)) { - stat.restore(stat.getDamage()); - stat.damage(floatValue); } - else if (prop == "modifier") - stat.setModifier(floatValue); - else if (prop == "progress") - stat.setProgress(floatValue * getMaxProgress(ptr, id, stat)); - stats.setSkill(id, stat); - } - }; - class AIStat - { - ObjectVariant mObject; - MWMechanics::AiSetting mIndex; + sol::object get(const Context& context, ESM::StringRefId attributeId) const + { + if (!mObject.ptr().getClass().isNpc()) + return sol::nil; - AIStat(ObjectVariant object, MWMechanics::AiSetting index) - : mObject(std::move(object)) - , mIndex(index) + return getValue(context, mObject, &setNpcValue, attributeId, "skillIncreasesForAttribute", + [attributeId](const MWWorld::Ptr& ptr) { + return ptr.getClass().getNpcStats(ptr).getSkillIncreasesForAttribute(attributeId); + }); + } + + void set(const Context& context, ESM::StringRefId attributeId, const sol::object& value) const + { + const auto& ptr = mObject.ptr(); + if (!ptr.getClass().isNpc()) + return; + + SelfObject* obj = mObject.asSelfObject(); + addStatUpdateAction(context.mLuaManager, *obj); + obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }] + = sol::main_object(value); + } + }; + + class SkillIncreasesForSpecializationStats { - } + ObjectVariant mObject; - public: - template - sol::object get(const Context& context, std::string_view prop, G getter) const - { - return getValue(context, mObject, &AIStat::setValue, static_cast(mIndex), prop, - [this, getter](const MWWorld::Ptr& ptr) { - return (ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex).*getter)(); - }); - } + public: + SkillIncreasesForSpecializationStats(ObjectVariant object) + : mObject(std::move(object)) + { + } - int getModified(const Context& context) const - { - auto base = LuaUtil::cast(get(context, "base", &MWMechanics::Stat::getBase)); - auto modifier = LuaUtil::cast(get(context, "modifier", &MWMechanics::Stat::getModifier)); - return std::max(0, base + modifier); - } + sol::object get(const Context& context, int specialization) const + { + if (!mObject.ptr().getClass().isNpc()) + return sol::nil; - static std::optional create(ObjectVariant object, MWMechanics::AiSetting index) - { - if (!object.ptr().getClass().isActor()) - return {}; - return AIStat{ std::move(object), index }; - } + return getValue(context, mObject, &setNpcValue, specialization, "skillIncreasesForSpecialization", + [specialization](const MWWorld::Ptr& ptr) { + return ptr.getClass().getNpcStats(ptr).getSkillIncreasesForSpecialization( + static_cast(specialization)); + }); + } - 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); - } + void set(const Context& context, int specialization, const sol::object& value) const + { + const auto& ptr = mObject.ptr(); + if (!ptr.getClass().isNpc()) + return; - static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + SelfObject* obj = mObject.asSelfObject(); + addStatUpdateAction(context.mLuaManager, *obj); + obj->mStatsCache[SelfObject::CachedStat{ + &setNpcValue, specialization, "skillIncreasesForSpecialization" }] + = sol::main_object(value); + } + }; + + class LevelStat { - auto index = static_cast(std::get(i)); - auto& stats = ptr.getClass().getCreatureStats(ptr); - auto stat = stats.getAiSetting(index); - int intValue = LuaUtil::cast(value); - if (prop == "base") - stat.setBase(intValue); - else if (prop == "modifier") - stat.setModifier(intValue); - stats.setAiSetting(index, stat); - } - }; + ObjectVariant mObject; + + LevelStat(ObjectVariant object) + : mObject(std::move(object)) + { + } + + public: + sol::object getCurrent(const Context& context) const + { + return getValue(context, mObject, &setCreatureValue, std::monostate{}, "current", + [](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); }); + } + + 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); + } + + sol::object getProgress(const Context& context) const + { + if (!mObject.ptr().getClass().isNpc()) + return sol::nil; + + return getValue(context, mObject, &setNpcValue, std::monostate{}, "progress", + [](const MWWorld::Ptr& ptr) { return ptr.getClass().getNpcStats(ptr).getLevelProgress(); }); + } + + void setProgress(const Context& context, const sol::object& value) const + { + const auto& ptr = mObject.ptr(); + if (!ptr.getClass().isNpc()) + return; + + SelfObject* obj = mObject.asSelfObject(); + addStatUpdateAction(context.mLuaManager, *obj); + obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }] + = sol::main_object(value); + } + + SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const + { + return SkillIncreasesForAttributeStats{ mObject }; + } + + SkillIncreasesForSpecializationStats getSkillIncreasesForSpecializationStats() const + { + return SkillIncreasesForSpecializationStats{ mObject }; + } + + static std::optional create(ObjectVariant object, Index) + { + if (!object.ptr().getClass().isActor()) + return {}; + return LevelStat{ std::move(object) }; + } + }; + + class DynamicStat + { + ObjectVariant mObject; + int mIndex; + + DynamicStat(ObjectVariant object, int index) + : mObject(std::move(object)) + , mIndex(index) + { + } + + public: + template + sol::object get(const Context& context, std::string_view prop, G getter) const + { + return getValue( + context, mObject, &DynamicStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) { + return (ptr.getClass().getCreatureStats(ptr).getDynamic(mIndex).*getter)(); + }); + } + + static std::optional create(ObjectVariant object, Index i) + { + if (!object.ptr().getClass().isActor()) + return {}; + int index = std::get(i); + return DynamicStat{ std::move(object), index }; + } + + 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); + } + + static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + int index = std::get(i); + auto& stats = ptr.getClass().getCreatureStats(ptr); + auto stat = stats.getDynamic(index); + float floatValue = LuaUtil::cast(value); + if (prop == "base") + stat.setBase(floatValue); + else if (prop == "current") + stat.setCurrent(floatValue, true, true); + else if (prop == "modifier") + stat.setModifier(floatValue); + stats.setDynamic(index, stat); + } + }; + + class AttributeStat + { + ObjectVariant mObject; + ESM::RefId mId; + + AttributeStat(ObjectVariant object, ESM::RefId id) + : mObject(std::move(object)) + , mId(id) + { + } + + public: + template + sol::object get(const Context& context, std::string_view prop, G getter) const + { + return getValue( + context, mObject, &AttributeStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) { + return (ptr.getClass().getCreatureStats(ptr).getAttribute(mId).*getter)(); + }); + } + + float getModified(const Context& context) const + { + auto base = LuaUtil::cast(get(context, "base", &MWMechanics::AttributeValue::getBase)); + auto damage = LuaUtil::cast(get(context, "damage", &MWMechanics::AttributeValue::getDamage)); + auto modifier + = LuaUtil::cast(get(context, "modifier", &MWMechanics::AttributeValue::getModifier)); + return std::max(0.f, base - damage + modifier); // Should match AttributeValue::getModified + } + + static std::optional create(ObjectVariant object, Index i) + { + if (!object.ptr().getClass().isActor()) + return {}; + ESM::RefId id = std::get(i); + return AttributeStat{ std::move(object), id }; + } + + 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); + } + + static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + ESM::RefId id = std::get(i); + auto& stats = ptr.getClass().getCreatureStats(ptr); + auto stat = stats.getAttribute(id); + float floatValue = LuaUtil::cast(value); + if (prop == "base") + stat.setBase(floatValue); + else if (prop == "damage") + { + stat.restore(stat.getDamage()); + stat.damage(floatValue); + } + else if (prop == "modifier") + stat.setModifier(floatValue); + stats.setAttribute(id, stat); + } + }; + + class SkillStat + { + ObjectVariant mObject; + ESM::RefId mId; + + SkillStat(ObjectVariant object, ESM::RefId id) + : mObject(std::move(object)) + , mId(id) + { + } + + static float getProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat) + { + float progress = stat.getProgress(); + if (progress != 0.f) + progress /= getMaxProgress(ptr, id, stat); + return progress; + } + + static float getMaxProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat) + { + const auto& store = *MWBase::Environment::get().getESMStore(); + const auto cl = store.get().find(ptr.get()->mBase->mClass); + return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(id, *cl); + } + + public: + template + sol::object get(const Context& context, std::string_view prop, G getter) const + { + return getValue( + context, mObject, &SkillStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) { + return (ptr.getClass().getNpcStats(ptr).getSkill(mId).*getter)(); + }); + } + + float getModified(const Context& context) const + { + auto base = LuaUtil::cast(get(context, "base", &MWMechanics::SkillValue::getBase)); + auto damage = LuaUtil::cast(get(context, "damage", &MWMechanics::SkillValue::getDamage)); + auto modifier = LuaUtil::cast(get(context, "modifier", &MWMechanics::SkillValue::getModifier)); + return std::max(0.f, base - damage + modifier); // Should match SkillValue::getModified + } + + sol::object getProgress(const Context& context) const + { + return getValue( + context, mObject, &SkillStat::setValue, mId, "progress", [this](const MWWorld::Ptr& ptr) { + return getProgress(ptr, mId, ptr.getClass().getNpcStats(ptr).getSkill(mId)); + }); + } + + static std::optional create(ObjectVariant object, Index index) + { + if (!object.ptr().getClass().isNpc()) + return {}; + ESM::RefId id = std::get(index); + return SkillStat{ std::move(object), id }; + } + + 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); + } + + static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + ESM::RefId id = std::get(index); + auto& stats = ptr.getClass().getNpcStats(ptr); + auto stat = stats.getSkill(id); + float floatValue = LuaUtil::cast(value); + if (prop == "base") + stat.setBase(floatValue); + else if (prop == "damage") + { + stat.restore(stat.getDamage()); + stat.damage(floatValue); + } + else if (prop == "modifier") + stat.setModifier(floatValue); + else if (prop == "progress") + stat.setProgress(floatValue * getMaxProgress(ptr, id, stat)); + stats.setSkill(id, stat); + } + }; + + class AIStat + { + ObjectVariant mObject; + MWMechanics::AiSetting mIndex; + + AIStat(ObjectVariant object, MWMechanics::AiSetting index) + : mObject(std::move(object)) + , mIndex(index) + { + } + + public: + template + sol::object get(const Context& context, std::string_view prop, G getter) const + { + return getValue(context, mObject, &AIStat::setValue, static_cast(mIndex), prop, + [this, getter](const MWWorld::Ptr& ptr) { + return (ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex).*getter)(); + }); + } + + int getModified(const Context& context) const + { + auto base = LuaUtil::cast(get(context, "base", &MWMechanics::Stat::getBase)); + auto modifier = LuaUtil::cast(get(context, "modifier", &MWMechanics::Stat::getModifier)); + return std::max(0, base + modifier); + } + + static std::optional create(ObjectVariant object, MWMechanics::AiSetting index) + { + if (!object.ptr().getClass().isActor()) + return {}; + return AIStat{ std::move(object), index }; + } + + 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); + } + + static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + auto index = static_cast(std::get(i)); + auto& stats = ptr.getClass().getCreatureStats(ptr); + auto stat = stats.getAiSetting(index); + int intValue = LuaUtil::cast(value); + if (prop == "base") + stat.setBase(intValue); + else if (prop == "modifier") + stat.setModifier(intValue); + stats.setAiSetting(index, stat); + } + }; + } } namespace sol