diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b823674d31..3d6469fa33 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -59,8 +59,8 @@ add_openmw_dir (mwscript add_openmw_dir (mwlua luamanagerimp actions object worldview userdataserializer eventqueue luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings - camerabindings uibindings inputbindings nearbybindings - types/types types/door types/actor types/container types/weapon + camerabindings uibindings inputbindings nearbybindings stats + types/types types/door types/actor types/container types/weapon types/npc ) add_openmw_dir (mwsound diff --git a/apps/openmw/mwlua/actions.cpp b/apps/openmw/mwlua/actions.cpp index 8135d14299..404bae6c6d 100644 --- a/apps/openmw/mwlua/actions.cpp +++ b/apps/openmw/mwlua/actions.cpp @@ -1,5 +1,7 @@ #include "actions.hpp" +#include "localscripts.hpp" + #include #include @@ -168,4 +170,11 @@ namespace MWLua std::string(" actor=") + idToString(mActor); } + void StatUpdateAction::apply(WorldView& worldView) const + { + LObject obj(mId, worldView.getObjectRegistry()); + LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); + if (scripts) + scripts->applyStatsCache(); + } } diff --git a/apps/openmw/mwlua/actions.hpp b/apps/openmw/mwlua/actions.hpp index 97da953437..30211b6e53 100644 --- a/apps/openmw/mwlua/actions.hpp +++ b/apps/openmw/mwlua/actions.hpp @@ -79,6 +79,16 @@ namespace MWLua ObjectId mActor; }; + class StatUpdateAction final : public Action + { + ObjectId mId; + public: + StatUpdateAction(LuaUtil::LuaState* state, ObjectId id) : Action(state), mId(id) {} + + void apply(WorldView& worldView) const override; + + std::string toString() const override { return "StatUpdateAction"; } + }; } #endif // MWLUA_ACTIONS_H diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index 7cdb31f518..948bc1e869 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -182,4 +182,11 @@ namespace MWLua }, event); } + void LocalScripts::applyStatsCache() + { + const auto& ptr = mData.ptr(); + for (auto& [stat, value] : mData.mStatsCache) + stat(ptr, value); + mData.mStatsCache.clear(); + } } diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index 10b9cd53af..b5bd8f95c8 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -26,8 +26,31 @@ namespace MWLua struct SelfObject : public LObject { + class CachedStat + { + public: + using Setter = void(*)(int, std::string_view, const MWWorld::Ptr&, const sol::object&); + private: + Setter mSetter; // Function that updates a stat's property + int mIndex; // Optional index to disambiguate the stat + std::string_view mProp; // Name of the stat's property + public: + CachedStat(Setter setter, int index, std::string_view prop) : mSetter(setter), mIndex(index), mProp(std::move(prop)) {} + + void operator()(const MWWorld::Ptr& ptr, const sol::object& object) const + { + mSetter(mIndex, mProp, ptr, object); + } + + bool operator<(const CachedStat& other) const + { + return std::tie(mSetter, mIndex, mProp) < std::tie(other.mSetter, other.mIndex, other.mProp); + } + }; + SelfObject(const LObject& obj) : LObject(obj), mIsActive(false) {} MWBase::LuaManager::ActorControls mControls; + std::map mStatsCache; bool mIsActive; }; @@ -45,6 +68,7 @@ namespace MWLua void receiveEngineEvent(const EngineEvent&); + void applyStatsCache(); protected: SelfObject mData; diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index a46435b2ca..9f35866f89 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -41,7 +41,7 @@ namespace MWLua { auto* lua = context.mLua; sol::table api(lua->sol(), sol::create); - api["API_REVISION"] = 18; + api["API_REVISION"] = 19; api["quit"] = [lua]() { Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback(); diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp new file mode 100644 index 0000000000..36d7cf6538 --- /dev/null +++ b/apps/openmw/mwlua/stats.cpp @@ -0,0 +1,369 @@ +#include "stats.hpp" + +#include +#include +#include +#include + +#include +#include + +#include "localscripts.hpp" +#include "luamanagerimp.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +namespace +{ + template + auto addIndexedAccessor(int index) + { + return sol::overload( + [index](MWLua::LocalScripts::SelfObject& o) { return T::create(&o, index); }, + [index](const MWLua::LObject& o) { return T::create(o, index); }, + [index](const MWLua::GObject& o) { return T::create(o, index); } + ); + } + + template + void addProp(const MWLua::Context& context, sol::usertype& type, std::string_view prop, G getter, bool readOnly = false) + { + if(readOnly) + type[prop] = sol::property([=](const T& stat) { return stat.get(context, prop, getter); }); + else + type[prop] = sol::property( + [=](const T& stat) { return stat.get(context, prop, getter); }, + [=](const T& stat, const sol::object& value) { stat.cache(context, prop, value); }); + } + + using SelfObject = MWLua::LocalScripts::SelfObject; + using StatObject = std::variant; + + const MWLua::Object* getObject(const StatObject& obj) + { + return std::visit([] (auto&& variant) -> const MWLua::Object* + { + using T = std::decay_t; + if constexpr(std::is_same_v) + return variant; + else if constexpr(std::is_same_v) + return &variant; + else if constexpr(std::is_same_v) + return &variant; + }, obj); + } + + template + sol::object getValue(const MWLua::Context& context, const StatObject& obj, SelfObject::CachedStat::Setter setter, int index, std::string_view prop, G getter) + { + return std::visit([&] (auto&& variant) + { + using T = std::decay_t; + if constexpr(std::is_same_v) + { + auto it = variant->mStatsCache.find({ setter, index, prop }); + if(it != variant->mStatsCache.end()) + return it->second; + return sol::make_object(context.mLua->sol(), getter(variant)); + } + else if constexpr(std::is_same_v) + return sol::make_object(context.mLua->sol(), getter(&variant)); + else if constexpr(std::is_same_v) + return sol::make_object(context.mLua->sol(), getter(&variant)); + }, obj); + } +} + +namespace MWLua +{ + class LevelStat + { + StatObject mObject; + + LevelStat(StatObject object) : mObject(std::move(object)) {} + public: + sol::object getCurrent(const Context& context) const + { + return getValue(context, mObject, &LevelStat::setValue, 0, "current", [](const MWLua::Object* obj) + { + const auto& ptr = obj->ptr(); + return ptr.getClass().getCreatureStats(ptr).getLevel(); + }); + } + + void setCurrent(const Context& context, const sol::object& value) const + { + SelfObject* obj = std::get(mObject); + if(obj->mStatsCache.empty()) + context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + obj->mStatsCache[SelfObject::CachedStat{&LevelStat::setValue, 0, "current"}] = value; + } + + sol::object getProgress(const Context& context) const + { + const auto& ptr = getObject(mObject)->ptr(); + if(!ptr.getClass().isNpc()) + return sol::nil; + return sol::make_object(context.mLua->sol(), ptr.getClass().getNpcStats(ptr).getLevelProgress()); + } + + static std::optional create(StatObject object, int index) + { + if(!getObject(object)->ptr().getClass().isActor()) + return {}; + return LevelStat{std::move(object)}; + } + + static void setValue(int, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + auto& stats = ptr.getClass().getCreatureStats(ptr); + if(prop == "current") + stats.setLevel(value.as()); + } + }; + + class DynamicStat + { + StatObject mObject; + int mIndex; + + DynamicStat(StatObject 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, [=](const MWLua::Object* obj) + { + const auto& ptr = obj->ptr(); + return (ptr.getClass().getCreatureStats(ptr).getDynamic(mIndex).*getter)(); + }); + } + + static std::optional create(StatObject object, int index) + { + if(!getObject(object)->ptr().getClass().isActor()) + return {}; + return DynamicStat{std::move(object), index}; + } + + void cache(const Context& context, std::string_view prop, const sol::object& value) const + { + SelfObject* obj = std::get(mObject); + if(obj->mStatsCache.empty()) + context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + obj->mStatsCache[SelfObject::CachedStat{&DynamicStat::setValue, mIndex, prop}] = value; + } + + static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + auto& stats = ptr.getClass().getCreatureStats(ptr); + auto stat = stats.getDynamic(index); + float floatValue = value.as(); + 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 + { + StatObject mObject; + int mIndex; + + AttributeStat(StatObject 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, &AttributeStat::setValue, mIndex, prop, [=](const MWLua::Object* obj) + { + const auto& ptr = obj->ptr(); + return (ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex).*getter)(); + }); + } + + static std::optional create(StatObject object, int index) + { + if(!getObject(object)->ptr().getClass().isActor()) + return {}; + return AttributeStat{std::move(object), index}; + } + + void cache(const Context& context, std::string_view prop, const sol::object& value) const + { + SelfObject* obj = std::get(mObject); + if(obj->mStatsCache.empty()) + context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + obj->mStatsCache[SelfObject::CachedStat{&AttributeStat::setValue, mIndex, prop}] = value; + } + + static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + auto& stats = ptr.getClass().getCreatureStats(ptr); + auto stat = stats.getAttribute(index); + float floatValue = value.as(); + 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(index, stat); + } + }; + + class SkillStat + { + StatObject mObject; + int mIndex; + + SkillStat(StatObject object, int index) : mObject(std::move(object)), mIndex(index) {} + + static float getProgress(const MWWorld::Ptr& ptr, int index, const MWMechanics::SkillValue& stat) + { + float progress = stat.getProgress(); + if(progress != 0.f) + progress /= getMaxProgress(ptr, index, stat); + return progress; + } + + static float getMaxProgress(const MWWorld::Ptr& ptr, int index, const MWMechanics::SkillValue& stat) { + const auto& store = MWBase::Environment::get().getWorld()->getStore(); + const auto cl = store.get().find(ptr.get()->mBase->mClass); + return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(index, *cl); + } + public: + template + sol::object get(const Context& context, std::string_view prop, G getter) const + { + return getValue(context, mObject, &SkillStat::setValue, mIndex, prop, [=](const MWLua::Object* obj) + { + const auto& ptr = obj->ptr(); + return (ptr.getClass().getNpcStats(ptr).getSkill(mIndex).*getter)(); + }); + } + + sol::object getProgress(const Context& context) const + { + return getValue(context, mObject, &SkillStat::setValue, mIndex, "progress", [=](const MWLua::Object* obj) + { + const auto& ptr = obj->ptr(); + return getProgress(ptr, mIndex, ptr.getClass().getNpcStats(ptr).getSkill(mIndex)); + }); + } + + static std::optional create(StatObject object, int index) + { + if(!getObject(object)->ptr().getClass().isNpc()) + return {}; + return SkillStat{std::move(object), index}; + } + + void cache(const Context& context, std::string_view prop, const sol::object& value) const + { + SelfObject* obj = std::get(mObject); + if(obj->mStatsCache.empty()) + context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + obj->mStatsCache[SelfObject::CachedStat{&SkillStat::setValue, mIndex, prop}] = value; + } + + static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + { + auto& stats = ptr.getClass().getNpcStats(ptr); + auto stat = stats.getSkill(index); + float floatValue = value.as(); + 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, index, stat)); + stats.setSkill(index, stat); + } + }; +} + +namespace sol +{ + template <> + struct is_automagical : std::false_type {}; + template <> + struct is_automagical : std::false_type {}; + template <> + struct is_automagical : std::false_type {}; + template <> + struct is_automagical : std::false_type {}; +} + +namespace MWLua +{ + void addActorStatsBindings(sol::table& actor, const Context& context) + { + sol::table stats(context.mLua->sol(), sol::create); + actor["stats"] = LuaUtil::makeReadOnly(stats); + + auto levelStatT = context.mLua->sol().new_usertype("LevelStat"); + levelStatT["current"] = sol::property( + [context](const LevelStat& stat) { return stat.getCurrent(context); }, + [context](const LevelStat& stat, const sol::object& value) { stat.setCurrent(context, value); }); + levelStatT["progress"] = sol::property([context](const LevelStat& stat) { return stat.getProgress(context); }); + stats["level"] = addIndexedAccessor(0); + + auto dynamicStatT = context.mLua->sol().new_usertype("DynamicStat"); + addProp(context, dynamicStatT, "base", &MWMechanics::DynamicStat::getBase); + addProp(context, dynamicStatT, "current", &MWMechanics::DynamicStat::getCurrent); + addProp(context, dynamicStatT, "modifier", &MWMechanics::DynamicStat::getModifier); + sol::table dynamic(context.mLua->sol(), sol::create); + stats["dynamic"] = LuaUtil::makeReadOnly(dynamic); + dynamic["health"] = addIndexedAccessor(0); + dynamic["magicka"] = addIndexedAccessor(1); + dynamic["fatigue"] = addIndexedAccessor(2); + + auto attributeStatT = context.mLua->sol().new_usertype("AttributeStat"); + addProp(context, attributeStatT, "base", &MWMechanics::AttributeValue::getBase); + addProp(context, attributeStatT, "damage", &MWMechanics::AttributeValue::getDamage); + addProp(context, attributeStatT, "modified", &MWMechanics::AttributeValue::getModified, true); + addProp(context, attributeStatT, "modifier", &MWMechanics::AttributeValue::getModifier); + sol::table attributes(context.mLua->sol(), sol::create); + stats["attributes"] = LuaUtil::makeReadOnly(attributes); + for(int id = ESM::Attribute::Strength; id < ESM::Attribute::Length; ++id) + attributes[Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[id])] = addIndexedAccessor(id); + } + + void addNpcStatsBindings(sol::table& npc, const Context& context) + { + sol::table npcStats(context.mLua->sol(), sol::create); + sol::table baseMeta(context.mLua->sol(), sol::create); + baseMeta[sol::meta_function::index] = LuaUtil::getMutableFromReadOnly(npc["baseType"]["stats"]); + npcStats[sol::metatable_key] = baseMeta; + npc["stats"] = LuaUtil::makeReadOnly(npcStats); + + auto skillStatT = context.mLua->sol().new_usertype("SkillStat"); + addProp(context, skillStatT, "base", &MWMechanics::SkillValue::getBase); + addProp(context, skillStatT, "damage", &MWMechanics::SkillValue::getDamage); + addProp(context, skillStatT, "modified", &MWMechanics::SkillValue::getModified, true); + addProp(context, skillStatT, "modifier", &MWMechanics::SkillValue::getModifier); + skillStatT["progress"] = sol::property( + [context](const SkillStat& stat) { return stat.getProgress(context); }, + [context](const SkillStat& stat, const sol::object& value) { stat.cache(context, "progress", value); }); + sol::table skills(context.mLua->sol(), sol::create); + npcStats["skills"] = LuaUtil::makeReadOnly(skills); + for(int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id) + skills[Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id])] = addIndexedAccessor(id); + } +} diff --git a/apps/openmw/mwlua/stats.hpp b/apps/openmw/mwlua/stats.hpp new file mode 100644 index 0000000000..5108b710bf --- /dev/null +++ b/apps/openmw/mwlua/stats.hpp @@ -0,0 +1,12 @@ +#ifndef MWLUA_STATS_H +#define MWLUA_STATS_H + +#include "context.hpp" + +namespace MWLua +{ + void addActorStatsBindings(sol::table& actor, const Context& context); + void addNpcStatsBindings(sol::table& npc, const Context& context); +} + +#endif diff --git a/apps/openmw/mwlua/types/actor.cpp b/apps/openmw/mwlua/types/actor.cpp index d620472095..321aaa20e6 100644 --- a/apps/openmw/mwlua/types/actor.cpp +++ b/apps/openmw/mwlua/types/actor.cpp @@ -10,6 +10,7 @@ #include "../luabindings.hpp" #include "../localscripts.hpp" #include "../luamanagerimp.hpp" +#include "../stats.hpp" namespace MWLua { @@ -132,6 +133,8 @@ namespace MWLua } context.mLuaManager->addAction(std::make_unique(context.mLua, obj.id(), std::move(eqp))); }; + + addActorStatsBindings(actor, context); } } diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp new file mode 100644 index 0000000000..8dafbe1e8a --- /dev/null +++ b/apps/openmw/mwlua/types/npc.cpp @@ -0,0 +1,11 @@ +#include "types.hpp" + +#include "../stats.hpp" + +namespace MWLua +{ + void addNpcBindings(sol::table npc, const Context& context) + { + addNpcStatsBindings(npc, context); + } +} diff --git a/apps/openmw/mwlua/types/types.cpp b/apps/openmw/mwlua/types/types.cpp index a175031058..d696fb9715 100644 --- a/apps/openmw/mwlua/types/types.cpp +++ b/apps/openmw/mwlua/types/types.cpp @@ -156,7 +156,7 @@ namespace MWLua ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA}); addType(ObjectTypeName::Creature, {ESM::REC_CREA}, ObjectTypeName::Actor); - addType(ObjectTypeName::NPC, {ESM::REC_INTERNAL_PLAYER, ESM::REC_NPC_}, ObjectTypeName::Actor); + addNpcBindings(addType(ObjectTypeName::NPC, {ESM::REC_INTERNAL_PLAYER, ESM::REC_NPC_}, ObjectTypeName::Actor), context); addType(ObjectTypeName::Player, {ESM::REC_INTERNAL_PLAYER}, ObjectTypeName::NPC); addType(ObjectTypeName::Armor, {ESM::REC_ARMO}, ObjectTypeName::Item); diff --git a/apps/openmw/mwlua/types/types.hpp b/apps/openmw/mwlua/types/types.hpp index af861c0bb4..cb345b7d01 100644 --- a/apps/openmw/mwlua/types/types.hpp +++ b/apps/openmw/mwlua/types/types.hpp @@ -28,6 +28,7 @@ namespace MWLua void addDoorBindings(sol::table door, 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); } #endif // MWLUA_TYPES_H diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 917a83ee8c..b9b5df44d6 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -128,6 +128,287 @@ -- local Actor = require('openmw.types').Actor -- Actor.setEquipment(self, {}) -- unequip all +--- +-- @type LevelStat +-- @field #number current The actor's current level. +-- @field #number progress The NPC's level progress (read-only.) + +--- +-- @type DynamicStat +-- @field #number base +-- @field #number current +-- @field #number modifier + +--- +-- @type AttributeStat +-- @field #number base The actor's base attribute value. +-- @field #number damage The amount the attribute has been damaged. +-- @field #number modified The actor's current attribute value (read-only.) +-- @field #number modifier The attribute's modifier. + +--- +-- @type SkillStat +-- @field #number base The NPC's base skill value. +-- @field #number damage The amount the skill has been damaged. +-- @field #number modified The NPC's current skill value (read-only.) +-- @field #number modifier The skill's modifier. +-- @field #number progress [0-1] The NPC's skill progress. + +--- +-- @type DynamicStats + +--- +-- Health (returns @{#DynamicStat}) +-- @function [parent=#DynamicStats] health +-- @param openmw.core#GameObject actor +-- @return #DynamicStat + +--- +-- Magicka (returns @{#DynamicStat}) +-- @function [parent=#DynamicStats] magicka +-- @param openmw.core#GameObject actor +-- @return #DynamicStat + +--- +-- Fatigue (returns @{#DynamicStat}) +-- @function [parent=#DynamicStats] fatigue +-- @param openmw.core#GameObject actor +-- @return #DynamicStat + +--- +-- @type AttributeStats + +--- +-- Strength (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] strength +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- Intelligence (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] intelligence +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- Willpower (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] willpower +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- Agility (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] agility +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- Speed (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] speed +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- Endurance (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] endurance +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- Personality (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] personality +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- Luck (returns @{#AttributeStat}) +-- @function [parent=#AttributeStats] luck +-- @param openmw.core#GameObject actor +-- @return #AttributeStat + +--- +-- @type SkillStats + +--- +-- Block (returns @{#SkillStat}) +-- @function [parent=#SkillStats] block +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Armorer (returns @{#SkillStat}) +-- @function [parent=#SkillStats] armorer +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Medium Armor (returns @{#SkillStat}) +-- @function [parent=#SkillStats] mediumarmor +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Heavy Armor (returns @{#SkillStat}) +-- @function [parent=#SkillStats] heavyarmor +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Blunt Weapon (returns @{#SkillStat}) +-- @function [parent=#SkillStats] bluntweapon +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Long Blade (returns @{#SkillStat}) +-- @function [parent=#SkillStats] longblade +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Axe (returns @{#SkillStat}) +-- @function [parent=#SkillStats] axe +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Spear (returns @{#SkillStat}) +-- @function [parent=#SkillStats] spear +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Athletics (returns @{#SkillStat}) +-- @function [parent=#SkillStats] athletics +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Enchant (returns @{#SkillStat}) +-- @function [parent=#SkillStats] enchant +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Destruction (returns @{#SkillStat}) +-- @function [parent=#SkillStats] destruction +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Alteration (returns @{#SkillStat}) +-- @function [parent=#SkillStats] alteration +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Illusion (returns @{#SkillStat}) +-- @function [parent=#SkillStats] illusion +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Conjuration (returns @{#SkillStat}) +-- @function [parent=#SkillStats] conjuration +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Mysticism (returns @{#SkillStat}) +-- @function [parent=#SkillStats] mysticism +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Restoration (returns @{#SkillStat}) +-- @function [parent=#SkillStats] restoration +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Alchemy (returns @{#SkillStat}) +-- @function [parent=#SkillStats] alchemy +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Unarmored (returns @{#SkillStat}) +-- @function [parent=#SkillStats] unarmored +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Security (returns @{#SkillStat}) +-- @function [parent=#SkillStats] security +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Sneak (returns @{#SkillStat}) +-- @function [parent=#SkillStats] sneak +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Acrobatics (returns @{#SkillStat}) +-- @function [parent=#SkillStats] acrobatics +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Light Armor (returns @{#SkillStat}) +-- @function [parent=#SkillStats] lightarmor +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Short Blade (returns @{#SkillStat}) +-- @function [parent=#SkillStats] shortblade +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Marksman (returns @{#SkillStat}) +-- @function [parent=#SkillStats] marksman +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Mercantile (returns @{#SkillStat}) +-- @function [parent=#SkillStats] mercantile +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Speechcraft (returns @{#SkillStat}) +-- @function [parent=#SkillStats] speechcraft +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- Hand To Hand (returns @{#SkillStat}) +-- @function [parent=#SkillStats] handtohand +-- @param openmw.core#GameObject actor +-- @return #SkillStat + +--- +-- @type ActorStats +-- @field #DynamicStats dynamic +-- @field #AttributeStats attributes + +--- +-- Level (returns @{#LevelStat}) +-- @function [parent=#ActorStats] level +-- @param openmw.core#GameObject actor +-- @return #LevelStat + +--- The actor's stats. +-- @field [parent=#Actor] #ActorStats stats + +--- +-- @type NpcStats +-- @extends ActorStats +-- @field #SkillStats skills --- @{#Item} functions (all pickable items that can be placed to an inventory or container) @@ -167,6 +448,7 @@ -- @type NPC -- @extends #Actor -- @field #Actor baseType @{#Actor} +-- @field [parent=#NPC] #NpcStats stats --- -- Whether the object is an NPC or a Player.