diff options
author | psi29a <psi29a@gmail.com> | 2024-03-02 20:14:27 +0000 |
---|---|---|
committer | psi29a <psi29a@gmail.com> | 2024-03-02 20:14:27 +0000 |
commit | d168466034cbf09d82ffbc0ad8ab194fed104783 (patch) | |
tree | a2d7bda7587abb52563c812c3d5832336fd2d038 | |
parent | e6f64f5e71db6fe76431f269aca9f8deb0938f5d (diff) | |
parent | e54decc830fde07cdf95192e25f7689bdea34eee (diff) |
Merge branch 'raciallybound' into 'master'
Expose races to Lua
See merge request OpenMW/openmw!3863
-rw-r--r-- | apps/openmw/CMakeLists.txt | 2 | ||||
-rw-r--r-- | apps/openmw/mwlua/birthsignbindings.cpp | 10 | ||||
-rw-r--r-- | apps/openmw/mwlua/classbindings.cpp | 34 | ||||
-rw-r--r-- | apps/openmw/mwlua/factionbindings.cpp | 21 | ||||
-rw-r--r-- | apps/openmw/mwlua/idcollectionbindings.hpp | 25 | ||||
-rw-r--r-- | apps/openmw/mwlua/racebindings.cpp | 117 | ||||
-rw-r--r-- | apps/openmw/mwlua/racebindings.hpp | 13 | ||||
-rw-r--r-- | apps/openmw/mwlua/types/npc.cpp | 2 | ||||
-rw-r--r-- | files/lua_api/openmw/types.lua | 34 |
9 files changed, 203 insertions, 55 deletions
diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index cf9f265730..5fb06881ec 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -64,7 +64,7 @@ add_openmw_dir (mwlua context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings - classbindings itemdata inputprocessor animationbindings birthsignbindings + classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings 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 diff --git a/apps/openmw/mwlua/birthsignbindings.cpp b/apps/openmw/mwlua/birthsignbindings.cpp index e569bc1b8f..6993ae0105 100644 --- a/apps/openmw/mwlua/birthsignbindings.cpp +++ b/apps/openmw/mwlua/birthsignbindings.cpp @@ -8,6 +8,7 @@ #include "../mwworld/esmstore.hpp" #include "birthsignbindings.hpp" +#include "idcollectionbindings.hpp" #include "luamanagerimp.hpp" #include "types/types.hpp" @@ -17,10 +18,6 @@ namespace sol struct is_automagical<ESM::BirthSign> : std::false_type { }; - template <> - struct is_automagical<MWWorld::Store<ESM::BirthSign>> : std::false_type - { - }; } namespace MWLua @@ -44,10 +41,7 @@ namespace MWLua return Misc::ResourceHelpers::correctTexturePath(rec.mTexture, vfs); }); signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table { - sol::table res(lua, sol::create); - for (size_t i = 0; i < rec.mPowers.mList.size(); ++i) - res[i + 1] = rec.mPowers.mList[i].serializeText(); - return res; + return createReadOnlyRefIdTable(lua, rec.mPowers.mList); }); return LuaUtil::makeReadOnly(birthSigns); diff --git a/apps/openmw/mwlua/classbindings.cpp b/apps/openmw/mwlua/classbindings.cpp index ea1ea8e7ef..c9d5a9fb7b 100644 --- a/apps/openmw/mwlua/classbindings.cpp +++ b/apps/openmw/mwlua/classbindings.cpp @@ -6,6 +6,7 @@ #include "../mwworld/esmstore.hpp" #include "classbindings.hpp" +#include "idcollectionbindings.hpp" #include "luamanagerimp.hpp" #include "stats.hpp" #include "types/types.hpp" @@ -16,10 +17,6 @@ namespace sol struct is_automagical<ESM::Class> : std::false_type { }; - template <> - struct is_automagical<MWWorld::Store<ESM::Class>> : std::false_type - { - }; } namespace MWLua @@ -40,34 +37,15 @@ namespace MWLua = 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; + return createReadOnlyRefIdTable(lua, rec.mData.mAttribute, ESM::Attribute::indexToRefId); }); 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; + return createReadOnlyRefIdTable( + lua, rec.mData.mSkills, [](const auto& pair) { return ESM::Skill::indexToRefId(pair[1]); }); }); 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; + return createReadOnlyRefIdTable( + lua, rec.mData.mSkills, [](const auto& pair) { return ESM::Skill::indexToRefId(pair[0]); }); }); classT["specialization"] = sol::readonly_property([](const ESM::Class& rec) -> std::string_view { diff --git a/apps/openmw/mwlua/factionbindings.cpp b/apps/openmw/mwlua/factionbindings.cpp index 87ce6ced39..e4c65386bf 100644 --- a/apps/openmw/mwlua/factionbindings.cpp +++ b/apps/openmw/mwlua/factionbindings.cpp @@ -9,6 +9,7 @@ #include "../mwmechanics/npcstats.hpp" +#include "idcollectionbindings.hpp" #include "luamanagerimp.hpp" namespace @@ -96,26 +97,10 @@ namespace MWLua return res; }); factionT["attributes"] = sol::readonly_property([&lua](const ESM::Faction& rec) { - sol::table res(lua, sol::create); - for (auto attributeIndex : rec.mData.mAttribute) - { - ESM::RefId id = ESM::Attribute::indexToRefId(attributeIndex); - if (!id.empty()) - res.add(id.serializeText()); - } - - return res; + return createReadOnlyRefIdTable(lua, rec.mData.mAttribute, ESM::Attribute::indexToRefId); }); factionT["skills"] = sol::readonly_property([&lua](const ESM::Faction& rec) { - sol::table res(lua, sol::create); - for (auto skillIndex : rec.mData.mSkills) - { - ESM::RefId id = ESM::Skill::indexToRefId(skillIndex); - if (!id.empty()) - res.add(id.serializeText()); - } - - return res; + return createReadOnlyRefIdTable(lua, rec.mData.mSkills, ESM::Skill::indexToRefId); }); auto rankT = lua.new_usertype<FactionRank>("ESM3_FactionRank"); rankT[sol::meta_function::to_string] = [](const FactionRank& rec) -> std::string { diff --git a/apps/openmw/mwlua/idcollectionbindings.hpp b/apps/openmw/mwlua/idcollectionbindings.hpp new file mode 100644 index 0000000000..15e2b14fb9 --- /dev/null +++ b/apps/openmw/mwlua/idcollectionbindings.hpp @@ -0,0 +1,25 @@ +#ifndef MWLUA_IDCOLLECTIONBINDINGS_H
+#define MWLUA_IDCOLLECTIONBINDINGS_H
+
+#include <functional>
+
+#include <components/esm/refid.hpp>
+#include <components/lua/luastate.hpp>
+
+namespace MWLua
+{
+ template <class C, class P = std::identity>
+ sol::table createReadOnlyRefIdTable(const sol::state_view& lua, const C& container, P projection = {})
+ {
+ sol::table res(lua, sol::create);
+ for (const auto& element : container)
+ {
+ ESM::RefId id = projection(element);
+ if (!id.empty())
+ res.add(id.serializeText());
+ }
+ return LuaUtil::makeReadOnly(res);
+ }
+}
+
+#endif
diff --git a/apps/openmw/mwlua/racebindings.cpp b/apps/openmw/mwlua/racebindings.cpp new file mode 100644 index 0000000000..e2e2ae2a8a --- /dev/null +++ b/apps/openmw/mwlua/racebindings.cpp @@ -0,0 +1,117 @@ +#include <components/esm/attr.hpp> +#include <components/esm3/loadrace.hpp> +#include <components/lua/luastate.hpp> + +#include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "idcollectionbindings.hpp" +#include "luamanagerimp.hpp" +#include "racebindings.hpp" +#include "types/types.hpp" + +namespace +{ + struct RaceAttributes + { + const ESM::Race& mRace; + const sol::state_view mLua; + + sol::table getAttribute(ESM::RefId id) const + { + sol::table res(mLua, sol::create); + res["male"] = mRace.mData.getAttribute(id, true); + res["female"] = mRace.mData.getAttribute(id, false); + return LuaUtil::makeReadOnly(res); + } + }; +} + +namespace sol +{ + template <> + struct is_automagical<ESM::Race> : std::false_type + { + }; + template <> + struct is_automagical<RaceAttributes> : std::false_type + { + }; +} + +namespace MWLua +{ + sol::table initRaceRecordBindings(const Context& context) + { + sol::state_view& lua = context.mLua->sol(); + sol::table races(context.mLua->sol(), sol::create); + addRecordFunctionBinding<ESM::Race>(races, context); + + auto raceT = lua.new_usertype<ESM::Race>("ESM3_Race"); + raceT[sol::meta_function::to_string] + = [](const ESM::Race& rec) -> std::string { return "ESM3_Race[" + rec.mId.toDebugString() + "]"; }; + raceT["id"] = sol::readonly_property([](const ESM::Race& rec) { return rec.mId.serializeText(); }); + raceT["name"] = sol::readonly_property([](const ESM::Race& rec) -> std::string_view { return rec.mName; }); + raceT["description"] + = sol::readonly_property([](const ESM::Race& rec) -> std::string_view { return rec.mDescription; }); + raceT["spells"] = sol::readonly_property( + [lua](const ESM::Race& rec) -> sol::table { return createReadOnlyRefIdTable(lua, rec.mPowers.mList); }); + raceT["skills"] = sol::readonly_property([lua](const ESM::Race& rec) -> sol::table { + sol::table res(lua, sol::create); + for (const auto& skillBonus : rec.mData.mBonus) + { + ESM::RefId skill = ESM::Skill::indexToRefId(skillBonus.mSkill); + if (!skill.empty()) + res[skill.serializeText()] = skillBonus.mBonus; + } + return res; + }); + raceT["isPlayable"] = sol::readonly_property( + [](const ESM::Race& rec) -> bool { return rec.mData.mFlags & ESM::Race::Playable; }); + raceT["isBeast"] + = sol::readonly_property([](const ESM::Race& rec) -> bool { return rec.mData.mFlags & ESM::Race::Beast; }); + raceT["height"] = sol::readonly_property([lua](const ESM::Race& rec) -> sol::table { + sol::table res(lua, sol::create); + res["male"] = rec.mData.mMaleHeight; + res["female"] = rec.mData.mFemaleHeight; + return LuaUtil::makeReadOnly(res); + }); + raceT["weight"] = sol::readonly_property([lua](const ESM::Race& rec) -> sol::table { + sol::table res(lua, sol::create); + res["male"] = rec.mData.mMaleWeight; + res["female"] = rec.mData.mFemaleWeight; + return LuaUtil::makeReadOnly(res); + }); + + raceT["attributes"] = sol::readonly_property([lua](const ESM::Race& rec) -> RaceAttributes { + return { rec, lua }; + }); + + auto attributesT = lua.new_usertype<RaceAttributes>("ESM3_RaceAttributes"); + const auto& store = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>(); + attributesT[sol::meta_function::index] + = [&](const RaceAttributes& attributes, std::string_view stringId) -> sol::optional<sol::table> { + ESM::RefId id = ESM::RefId::deserializeText(stringId); + if (!store.search(id)) + return sol::nullopt; + return attributes.getAttribute(id); + }; + attributesT[sol::meta_function::pairs] = [&](sol::this_state ts, RaceAttributes& attributes) { + auto iterator = store.begin(); + return sol::as_function( + [iterator, attributes, + &store]() mutable -> std::pair<sol::optional<std::string>, sol::optional<sol::table>> { + if (iterator != store.end()) + { + ESM::RefId id = iterator->mId; + ++iterator; + return { id.serializeText(), attributes.getAttribute(id) }; + } + return { sol::nullopt, sol::nullopt }; + }); + }; + + return LuaUtil::makeReadOnly(races); + } +} diff --git a/apps/openmw/mwlua/racebindings.hpp b/apps/openmw/mwlua/racebindings.hpp new file mode 100644 index 0000000000..43ba9237c5 --- /dev/null +++ b/apps/openmw/mwlua/racebindings.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_RACEBINDINGS_H +#define MWLUA_RACEBINDINGS_H + +#include <sol/forward.hpp> + +#include "context.hpp" + +namespace MWLua +{ + sol::table initRaceRecordBindings(const Context& context); +} + +#endif // MWLUA_RACEBINDINGS_H diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index d7d459bb81..29147a826b 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -15,6 +15,7 @@ #include "../classbindings.hpp" #include "../localscripts.hpp" +#include "../racebindings.hpp" #include "../stats.hpp" namespace sol @@ -86,6 +87,7 @@ namespace MWLua addActorServicesBindings<ESM::NPC>(record, context); npc["classes"] = initClassRecordBindings(context); + npc["races"] = initRaceRecordBindings(context); // This function is game-specific, in future we should replace it with something more universal. npc["isWerewolf"] = [](const Object& o) { diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 0c51544f64..60f3e79628 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -958,6 +958,40 @@ -- @param #any objectOrRecordId -- @return #NpcRecord +--- @{#Races}: Race data +-- @field [parent=#NPC] #Races races + +--- +-- A read-only list of all @{#RaceRecord}s in the world database. +-- @field [parent=#Races] #list<#RaceRecord> records + +--- +-- Returns a read-only @{#RaceRecord} +-- @function [parent=#Races] record +-- @param #string recordId +-- @return #RaceRecord + +--- +-- Race data record +-- @type RaceRecord +-- @field #string id Race id +-- @field #string name Race name +-- @field #string description Race description +-- @field #map<#string, #number> skills A map of bonus skill points by skill ID +-- @field #list<#string> spells A read-only list containing the ids of all spells inherent to the race +-- @field #bool isPlayable True if the player can pick this race in character generation +-- @field #bool isBeast True if this race is a beast race +-- @field #GenderedNumber height Height values +-- @field #GenderedNumber weight Weight values +-- @field #map<#string, #GenderedNumber> attributes A read-only table of attribute ID to base value +-- @usage -- Get base strength for men +-- strength = types.NPC.races.records[1].attributes.strength.male + +--- +-- @type GenderedNumber +-- @field #number male Male value +-- @field #number female Female value + --- -- @type NpcRecord -- @field #string id The record ID of the NPC |