diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 57e2b9d708..5394f1ac30 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -64,7 +64,7 @@ add_openmw_dir (mwlua 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 - worker magicbindings factionbindings + worker magicbindings factionbindings classbindings ) add_openmw_dir (mwsound diff --git a/apps/openmw/mwlua/classbindings.cpp b/apps/openmw/mwlua/classbindings.cpp new file mode 100644 index 0000000000..5a9b3969d2 --- /dev/null +++ b/apps/openmw/mwlua/classbindings.cpp @@ -0,0 +1,106 @@ +#include "classbindings.hpp" + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "luamanagerimp.hpp" + +namespace +{ +} + +namespace sol +{ + template <> + struct is_automagical : std::false_type + { + }; + template <> + struct is_automagical> : std::false_type + { + }; +} + +namespace MWLua +{ + using classStore = MWWorld::Store; + + void initCoreClassBindings(const Context& context) + { + sol::state_view& lua = context.mLua->sol(); + sol::usertype classStoreT = lua.new_usertype("ESM3_classStore"); + classStoreT[sol::meta_function::to_string] = [](const classStore& store) { + return "ESM3_classStore{" + std::to_string(store.getSize()) + " classes}"; + }; + classStoreT[sol::meta_function::length] = [](const classStore& store) { return store.getSize(); }; + classStoreT[sol::meta_function::index] = sol::overload( + [](const classStore& store, size_t index) -> const ESM::Class* { + if (index == 0 || index > store.getSize()) + return nullptr; + return store.at(index - 1); + }, + [](const classStore& store, std::string_view classId) -> const ESM::Class* { + return store.search(ESM::RefId::deserializeText(classId)); + }); + classStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); + classStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + // class record + auto classT = lua.new_usertype("ESM3_Class"); + classT[sol::meta_function::to_string] + = [](const ESM::Class& rec) -> std::string { return "ESM3_Class[" + rec.mId.toDebugString() + "]"; }; + classT["id"] = sol::readonly_property([](const ESM::Class& rec) { return rec.mId.serializeText(); }); + classT["name"] = sol::readonly_property([](const ESM::Class& rec) -> std::string_view { return rec.mName; }); + classT["description"] + = sol::readonly_property([](const ESM::Class& rec) -> std::string_view { return rec.mDescription; }); + classT["majorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table { + sol::table res(lua, sol::create); + auto skills = rec.mData.mSkills; + + for (size_t i = 0; i < skills.size(); ++i) + { + ESM::RefId skillId = ESM::Skill::indexToRefId(skills[i][1]); + res[i + 1] = skillId.serializeText(); + } + + return res; + }); + classT["attributes"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table { + sol::table res(lua, sol::create); + auto attribute = rec.mData.mAttribute; + + for (size_t i = 0; i < attribute.size(); ++i) + { + ESM::RefId attributeId = ESM::Attribute::indexToRefId(attribute[i]); + res[i + 1] = attributeId.serializeText(); + } + + return res; + }); + classT["minorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table { + sol::table res(lua, sol::create); + auto skills = rec.mData.mSkills; + + for (size_t i = 0; i < skills.size(); ++i) + { + ESM::RefId skillId = ESM::Skill::indexToRefId(skills[i][0]); + res[i + 1] = skillId.serializeText(); + } + + return res; + }); + classT["specialization"] = sol::readonly_property([](const ESM::Class& rec) -> std::string_view { + if (rec.mData.mSpecialization == ESM::Class::Stealth) + return "stealth"; + else if (rec.mData.mSpecialization == ESM::Class::Magic) + return "magic"; + else + return "combat"; + }); + classT["isPlayable"] + = sol::readonly_property([](const ESM::Class& rec) -> bool { return rec.mData.mIsPlayable; }); + } +} diff --git a/apps/openmw/mwlua/classbindings.hpp b/apps/openmw/mwlua/classbindings.hpp new file mode 100644 index 0000000000..c907e5ea94 --- /dev/null +++ b/apps/openmw/mwlua/classbindings.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_CLASSBINDINGS_H +#define MWLUA_CLASSBINDINGS_H + +#include + +#include "context.hpp" + +namespace MWLua +{ + void initCoreClassBindings(const Context& context); +} + +#endif // MWLUA_CLASSBINDINGS_H diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index a50459502b..f506ecbc80 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include "camerabindings.hpp" #include "cellbindings.hpp" +#include "classbindings.hpp" #include "debugbindings.hpp" #include "factionbindings.hpp" #include "inputbindings.hpp" @@ -159,6 +161,9 @@ namespace MWLua initCoreFactionBindings(context); api["factions"] = &MWBase::Environment::get().getWorld()->getStore().get(); + initCoreClassBindings(context); + api["class"] = &MWBase::Environment::get().getWorld()->getStore().get(); + api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager()); const MWWorld::Store* gmstStore = &MWBase::Environment::get().getESMStore()->get(); diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 2c815d6dfc..304f0642f2 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -14,6 +14,10 @@ -- A read-only list of all @{#FactionRecord}s in the world database. -- @field [parent=#core] #list<#FactionRecord> factions +--- +-- A read-only list of all @{#ClassRecord}s in the world database. +-- @field [parent=#core] #list<#ClassRecord> class + --- -- Terminates the game and quits to the OS. Should be used only for testing purposes. -- @function [parent=#core] quit @@ -868,6 +872,18 @@ -- @field #string failureSound VFS path to the failure sound -- @field #string hitSound VFS path to the hit sound +--- +-- Class data record +-- @type ClassRecord +-- @field #string id Class id +-- @field #string name Class name +-- @field #list<#string> attributes A read-only list containing the specialized attributes of the class. +-- @field #list<#string> majorSkills A read-only list containing the major skills of the class. +-- @field #list<#string> minorSkills A read-only list containing the minor skills of the class. +-- @field #string description Class description +-- @field #string specialization Class specialization. Either combat, magic, or stealth. + + --- -- Faction data record -- @type FactionRecord