diff --git a/CMakeLists.txt b/CMakeLists.txt index e0687ec198..10d7fba386 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 49) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 50) +set(OPENMW_LUA_API_REVISION 51) set(OPENMW_POSTPROCESSING_API_REVISION 1) set(OPENMW_VERSION_COMMITHASH "") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a05d20af73..421cab852d 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..339b724f19 --- /dev/null +++ b/apps/openmw/mwlua/classbindings.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "classbindings.hpp" +#include "luamanagerimp.hpp" +#include "stats.hpp" +#include "types/types.hpp" + +namespace sol +{ + template <> + struct is_automagical : std::false_type + { + }; + template <> + struct is_automagical> : std::false_type + { + }; +} + +namespace MWLua +{ + + sol::table initCoreClassBindings(const Context& context) + { + sol::state_view& lua = context.mLua->sol(); + sol::table classes(context.mLua->sol(), sol::create); + addRecordFunctionBinding(classes, context); + + 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["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["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["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 { + return ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization); + }); + classT["isPlayable"] + = sol::readonly_property([](const ESM::Class& rec) -> bool { return rec.mData.mIsPlayable; }); + return LuaUtil::makeReadOnly(classes); + } +} diff --git a/apps/openmw/mwlua/classbindings.hpp b/apps/openmw/mwlua/classbindings.hpp new file mode 100644 index 0000000000..9dd9befae4 --- /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 +{ + sol::table initCoreClassBindings(const Context& context); +} + +#endif // MWLUA_CLASSBINDINGS_H diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 127d8e79d9..03822fd92a 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -446,6 +446,9 @@ namespace MWLua skillT["name"] = sol::readonly_property([](const ESM::Skill& rec) -> std::string_view { return rec.mName; }); skillT["description"] = sol::readonly_property([](const ESM::Skill& rec) -> std::string_view { return rec.mDescription; }); + skillT["specialization"] = sol::readonly_property([](const ESM::Skill& rec) -> std::string_view { + return ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization); + }); skillT["icon"] = sol::readonly_property([vfs](const ESM::Skill& rec) -> std::string { return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); }); diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 25997b6468..68d13342c4 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -12,6 +12,7 @@ #include #include +#include "../classbindings.hpp" #include "../localscripts.hpp" #include "../stats.hpp" @@ -81,6 +82,8 @@ namespace MWLua record["baseGold"] = sol::readonly_property([](const ESM::NPC& rec) -> int { return rec.mNpdt.mGold; }); addActorServicesBindings(record, context); + npc["classes"] = initCoreClassBindings(context); + // This function is game-specific, in future we should replace it with something more universal. npc["isWerewolf"] = [](const Object& o) { const MWWorld::Class& cls = o.ptr().getClass(); diff --git a/components/esm3/loadclas.cpp b/components/esm3/loadclas.cpp index 0c58f1d45a..ec4ff680fa 100644 --- a/components/esm3/loadclas.cpp +++ b/components/esm3/loadclas.cpp @@ -10,6 +10,7 @@ namespace ESM { const std::string_view Class::sGmstSpecializationIds[3] = { "sSpecializationCombat", "sSpecializationMagic", "sSpecializationStealth" }; + const std::array Class::specializationIndexToLuaId = { "combat", "magic", "stealth" }; int32_t& Class::CLDTstruct::getSkill(int index, bool major) { diff --git a/components/esm3/loadclas.hpp b/components/esm3/loadclas.hpp index 47b8b73b7e..a804a8da8e 100644 --- a/components/esm3/loadclas.hpp +++ b/components/esm3/loadclas.hpp @@ -32,6 +32,7 @@ namespace ESM }; static const std::string_view sGmstSpecializationIds[3]; + static const std::array specializationIndexToLuaId; struct CLDTstruct { diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 44ab4473fa..1a2ec106d9 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -861,6 +861,7 @@ -- @field #string name Human-readable name -- @field #string description Human-readable description -- @field #string icon VFS path to the icon +-- @field #string specialization Skill specialization. Either combat, magic, or stealth. -- @field #MagicSchoolData school Optional magic school -- @field #string attribute The id of the skill's governing attribute diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 14542529cc..12b43f748d 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -878,6 +878,31 @@ -- @param openmw.core#GameObject actor -- @return #number +--- @{#Classes}: Class Data +-- @field [parent=#NPC] #Classes classes + +--- +-- A read-only list of all @{#ClassRecord}s in the world database. +-- @field [parent=#Classes] #list<#ClassRecord> records + +--- +-- Returns a read-only @{#ClassRecord} +-- @function [parent=#Classes] record +-- @param #string recordId +-- @return #ClassRecord + +--- +-- 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 #boolean isPlayable True if the player can play as this class +-- @field #string specialization Class specialization. Either combat, magic, or stealth. + --- -- Whether the NPC or player is in the werewolf form at the moment. -- @function [parent=#NPC] isWerewolf