mirror of
https://github.com/OpenMW/openmw.git
synced 2026-01-14 13:00:54 +00:00
Deduplicate stats cache usage
This commit is contained in:
parent
9233f82113
commit
998065618f
4 changed files with 165 additions and 173 deletions
|
|
@ -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<ESM::Enchantment>().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<float>(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<int>(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<float>(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<std::string_view>(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<ESM::Creature>().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<ESM::Enchantment>().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<float>(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<int>(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<float>(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<std::string_view>(value);
|
||||
ESM::RefId creature = ESM::RefId::deserializeText(souldId);
|
||||
const auto& store = *MWBase::Environment::get().getESMStore();
|
||||
|
||||
// TODO: Add Support for NPC Souls
|
||||
if (store.get<ESM::Creature>().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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<CachedStat, sol::main_object> mStatsCache;
|
||||
bool mIsActive;
|
||||
|
||||
private:
|
||||
friend class LocalScripts;
|
||||
std::map<CachedStat, sol::main_object> mStatsCache;
|
||||
};
|
||||
|
||||
class LocalScripts : public LuaUtil::ScriptsContainer
|
||||
|
|
|
|||
|
|
@ -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<int>(mIndex), prop }]
|
||||
= sol::main_object(value);
|
||||
obj->cacheStat(*context.mLuaManager,
|
||||
SelfObject::CachedStat{ &AIStat::setValue, static_cast<int>(mIndex), prop }, value);
|
||||
}
|
||||
|
||||
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
|
|
|
|||
Loading…
Reference in a new issue