summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpsi29a <psi29a@gmail.com>2024-03-02 20:14:27 +0000
committerpsi29a <psi29a@gmail.com>2024-03-02 20:14:27 +0000
commitd168466034cbf09d82ffbc0ad8ab194fed104783 (patch)
treea2d7bda7587abb52563c812c3d5832336fd2d038
parente6f64f5e71db6fe76431f269aca9f8deb0938f5d (diff)
parente54decc830fde07cdf95192e25f7689bdea34eee (diff)
Merge branch 'raciallybound' into 'master'
Expose races to Lua See merge request OpenMW/openmw!3863
-rw-r--r--apps/openmw/CMakeLists.txt2
-rw-r--r--apps/openmw/mwlua/birthsignbindings.cpp10
-rw-r--r--apps/openmw/mwlua/classbindings.cpp34
-rw-r--r--apps/openmw/mwlua/factionbindings.cpp21
-rw-r--r--apps/openmw/mwlua/idcollectionbindings.hpp25
-rw-r--r--apps/openmw/mwlua/racebindings.cpp117
-rw-r--r--apps/openmw/mwlua/racebindings.hpp13
-rw-r--r--apps/openmw/mwlua/types/npc.cpp2
-rw-r--r--files/lua_api/openmw/types.lua34
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