summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvil Eye <malusluminis@hotmail.com>2024-02-22 19:29:19 +0100
committerEvil Eye <malusluminis@hotmail.com>2024-02-27 21:50:57 +0100
commitcd118ee2639c120ae7c5acb54d03adbb72a31b67 (patch)
tree9edae58d6ab7cf7a420068d9066aec7e25c18292
parentb9fc9f0827ecd21e8ba6a8161eb3116c742e5f8e (diff)
Expose races to Lua
-rw-r--r--apps/openmw/CMakeLists.txt2
-rw-r--r--apps/openmw/mwlua/birthsignbindings.cpp23
-rw-r--r--apps/openmw/mwlua/racebindings.cpp120
-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.lua29
6 files changed, 182 insertions, 7 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..95b427eaa4 100644
--- a/apps/openmw/mwlua/birthsignbindings.cpp
+++ b/apps/openmw/mwlua/birthsignbindings.cpp
@@ -21,6 +21,10 @@ namespace sol
struct is_automagical<MWWorld::Store<ESM::BirthSign>> : std::false_type
{
};
+ template <>
+ struct is_automagical<ESM::SpellList> : std::false_type
+ {
+ };
}
namespace MWLua
@@ -43,12 +47,19 @@ namespace MWLua
signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string {
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;
- });
+ signT["spells"]
+ = sol::readonly_property([](const ESM::BirthSign& rec) -> const ESM::SpellList* { return &rec.mPowers; });
+
+ auto spellListT = lua.new_usertype<ESM::SpellList>("ESM3_SpellList");
+ spellListT[sol::meta_function::length] = [](const ESM::SpellList& list) { return list.mList.size(); };
+ spellListT[sol::meta_function::index]
+ = [](const ESM::SpellList& list, size_t index) -> sol::optional<std::string> {
+ if (index == 0 || index > list.mList.size())
+ return sol::nullopt;
+ return list.mList[index - 1].serializeText(); // Translate from Lua's 1-based indexing.
+ };
+ spellListT[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
+ spellListT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
return LuaUtil::makeReadOnly(birthSigns);
}
diff --git a/apps/openmw/mwlua/racebindings.cpp b/apps/openmw/mwlua/racebindings.cpp
new file mode 100644
index 0000000000..b5ea3c12bf
--- /dev/null
+++ b/apps/openmw/mwlua/racebindings.cpp
@@ -0,0 +1,120 @@
+#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 "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<MWWorld::Store<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) -> const ESM::SpellList* { return &rec.mPowers; });
+ 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..d085b355c8 100644
--- a/files/lua_api/openmw/types.lua
+++ b/files/lua_api/openmw/types.lua
@@ -958,6 +958,35 @@
-- @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 #map<#string, #number> height A read-only table with male and female fields
+-- @field #map<#string, #number> weight A read-only table with male and female fields
+-- @field #map<#string, #map<#string, #number>> attributes A read-only table of attribute ID to male and female base values
+-- @usage -- Get base strength for men
+-- strength = types.NPC.races.records[1].attributes.strength.male
+
---
-- @type NpcRecord
-- @field #string id The record ID of the NPC