diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e69bb5f240..f92e8a0bc1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -63,7 +63,7 @@ add_openmw_dir (mwlua luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings - postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings + postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker magicbindings factionbindings classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings 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 diff --git a/apps/openmw/mwlua/corebindings.cpp b/apps/openmw/mwlua/corebindings.cpp index 8d8e97ed07..b212d4d01c 100644 --- a/apps/openmw/mwlua/corebindings.cpp +++ b/apps/openmw/mwlua/corebindings.cpp @@ -97,8 +97,7 @@ namespace MWLua api["magic"] = initCoreMagicBindings(context); api["stats"] = initCoreStatsBindings(context); - initCoreFactionBindings(context); - api["factions"] = &MWBase::Environment::get().getESMStore()->get(); + api["factions"] = initCoreFactionBindings(context); api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager()); const MWWorld::Store* gmstStore diff --git a/apps/openmw/mwlua/factionbindings.cpp b/apps/openmw/mwlua/factionbindings.cpp index b606d1a6f9..83b9cfc5e8 100644 --- a/apps/openmw/mwlua/factionbindings.cpp +++ b/apps/openmw/mwlua/factionbindings.cpp @@ -1,4 +1,5 @@ #include "factionbindings.hpp" +#include "recordstore.hpp" #include #include @@ -32,10 +33,6 @@ namespace sol { }; template <> - struct is_automagical> : std::false_type - { - }; - template <> struct is_automagical> : std::false_type { }; @@ -43,27 +40,11 @@ namespace sol namespace MWLua { - using FactionStore = MWWorld::Store; - - void initCoreFactionBindings(const Context& context) + sol::table initCoreFactionBindings(const Context& context) { sol::state_view& lua = context.mLua->sol(); - sol::usertype factionStoreT = lua.new_usertype("ESM3_FactionStore"); - factionStoreT[sol::meta_function::to_string] = [](const FactionStore& store) { - return "ESM3_FactionStore{" + std::to_string(store.getSize()) + " factions}"; - }; - factionStoreT[sol::meta_function::length] = [](const FactionStore& store) { return store.getSize(); }; - factionStoreT[sol::meta_function::index] = sol::overload( - [](const FactionStore& store, size_t index) -> const ESM::Faction* { - if (index == 0 || index > store.getSize()) - return nullptr; - return store.at(index - 1); - }, - [](const FactionStore& store, std::string_view factionId) -> const ESM::Faction* { - return store.search(ESM::RefId::deserializeText(factionId)); - }); - factionStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); - factionStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + sol::table factions(lua, sol::create); + addRecordFunctionBinding(factions, context); // Faction record auto factionT = lua.new_usertype("ESM3_Faction"); factionT[sol::meta_function::to_string] @@ -113,5 +94,6 @@ namespace MWLua res.add(rec.mAttribute2); return res; }); + return LuaUtil::makeReadOnly(factions); } } diff --git a/apps/openmw/mwlua/factionbindings.hpp b/apps/openmw/mwlua/factionbindings.hpp index fe37133dbe..0dc06ceaf2 100644 --- a/apps/openmw/mwlua/factionbindings.hpp +++ b/apps/openmw/mwlua/factionbindings.hpp @@ -7,7 +7,7 @@ namespace MWLua { - void initCoreFactionBindings(const Context& context); + sol::table initCoreFactionBindings(const Context& context); } #endif // MWLUA_FACTIONBINDINGS_H diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index 1e3cb2ab69..9dae9f085f 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -31,6 +31,7 @@ #include "luamanagerimp.hpp" #include "object.hpp" #include "objectvariant.hpp" +#include "recordstore.hpp" namespace MWLua { @@ -135,10 +136,6 @@ namespace MWLua namespace sol { - template - struct is_automagical> : std::false_type - { - }; template <> struct is_automagical : std::false_type { @@ -228,50 +225,18 @@ namespace MWLua } // Spell store - using SpellStore = MWWorld::Store; - const SpellStore* spellStore = &MWBase::Environment::get().getWorld()->getStore().get(); - sol::usertype spellStoreT = lua.new_usertype("ESM3_SpellStore"); - spellStoreT[sol::meta_function::to_string] - = [](const SpellStore& store) { return "ESM3_SpellStore{" + std::to_string(store.getSize()) + " spells}"; }; - spellStoreT[sol::meta_function::length] = [](const SpellStore& store) { return store.getSize(); }; - spellStoreT[sol::meta_function::index] = sol::overload( - [](const SpellStore& store, size_t index) -> const ESM::Spell* { - if (index == 0 || index > store.getSize()) - return nullptr; - return store.at(index - 1); - }, - [](const SpellStore& store, std::string_view spellId) -> const ESM::Spell* { - return store.search(ESM::RefId::deserializeText(spellId)); - }); - spellStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); - spellStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); - - magicApi["spells"] = spellStore; + sol::table spells(lua, sol::create); + addRecordFunctionBinding(spells, context); + magicApi["spells"] = LuaUtil::makeReadOnly(spells); // Enchantment store - using EnchantmentStore = MWWorld::Store; - const EnchantmentStore* enchantmentStore - = &MWBase::Environment::get().getWorld()->getStore().get(); - sol::usertype enchantmentStoreT = lua.new_usertype("ESM3_EnchantmentStore"); - enchantmentStoreT[sol::meta_function::to_string] = [](const EnchantmentStore& store) { - return "ESM3_EnchantmentStore{" + std::to_string(store.getSize()) + " enchantments}"; - }; - enchantmentStoreT[sol::meta_function::length] = [](const EnchantmentStore& store) { return store.getSize(); }; - enchantmentStoreT[sol::meta_function::index] = sol::overload( - [](const EnchantmentStore& store, size_t index) -> const ESM::Enchantment* { - if (index == 0 || index > store.getSize()) - return nullptr; - return store.at(index - 1); - }, - [](const EnchantmentStore& store, std::string_view enchantmentId) -> const ESM::Enchantment* { - return store.search(ESM::RefId::deserializeText(enchantmentId)); - }); - enchantmentStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); - enchantmentStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); - - magicApi["enchantments"] = enchantmentStore; + sol::table enchantments(lua, sol::create); + addRecordFunctionBinding(enchantments, context); + magicApi["enchantments"] = LuaUtil::makeReadOnly(enchantments); // MagicEffect store + sol::table magicEffects(lua, sol::create); + magicApi["effects"] = LuaUtil::makeReadOnly(magicEffects); using MagicEffectStore = MWWorld::Store; const MagicEffectStore* magicEffectStore = &MWBase::Environment::get().getWorld()->getStore().get(); @@ -303,8 +268,10 @@ namespace MWLua }; magicEffectStoreT[sol::meta_function::pairs] = [iter = sol::make_object(lua, magicEffectsIter)] { return iter; }; + magicEffectStoreT[sol::meta_function::ipairs] + = [iter = sol::make_object(lua, magicEffectsIter)] { return iter; }; - magicApi["effects"] = magicEffectStore; + magicEffects["records"] = magicEffectStore; // Spell record auto spellT = lua.new_usertype("ESM3_Spell"); diff --git a/apps/openmw/mwlua/recordstore.hpp b/apps/openmw/mwlua/recordstore.hpp new file mode 100644 index 0000000000..3d04de396b --- /dev/null +++ b/apps/openmw/mwlua/recordstore.hpp @@ -0,0 +1,63 @@ +#ifndef MWLUA_RECORDSTORE_H +#define MWLUA_RECORDSTORE_H + +#include + +#include +#include + +#include "apps/openmw/mwbase/environment.hpp" +#include "apps/openmw/mwbase/world.hpp" +#include "apps/openmw/mwworld/esmstore.hpp" +#include "apps/openmw/mwworld/store.hpp" + +#include "context.hpp" +#include "object.hpp" + +namespace sol +{ + // Ensure sol does not try to create the automatic Container or usertype bindings for Store. + // They include write operations and we want the store to be read-only. + template + struct is_automagical> : std::false_type + { + }; +} + +namespace MWLua +{ + template + void addRecordFunctionBinding( + sol::table& table, const Context& context, const std::string& recordName = std::string(T::getRecordType())) + { + const MWWorld::Store& store = MWBase::Environment::get().getESMStore()->get(); + + table["record"] = sol::overload([](const Object& obj) -> const T* { return obj.ptr().get()->mBase; }, + [&store](std::string_view id) -> const T* { return store.search(ESM::RefId::deserializeText(id)); }); + + // Define a custom user type for the store. + // Provide the interface of a read-only array. + using StoreT = MWWorld::Store; + sol::state_view& lua = context.mLua->sol(); + sol::usertype storeT = lua.new_usertype(recordName + "WorldStore"); + storeT[sol::meta_function::to_string] = [recordName](const StoreT& store) { + return "{" + std::to_string(store.getSize()) + " " + recordName + " records}"; + }; + storeT[sol::meta_function::length] = [](const StoreT& store) { return store.getSize(); }; + storeT[sol::meta_function::index] = sol::overload( + [](const StoreT& store, size_t index) -> const T* { + if (index == 0 || index > store.getSize()) + return nullptr; + return store.at(index - 1); // Translate from Lua's 1-based indexing. + }, + [](const StoreT& store, std::string_view id) -> const T* { + return store.search(ESM::RefId::deserializeText(id)); + }); + storeT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + storeT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); + + // Provide access to the store. + table["records"] = &store; + } +} +#endif // MWLUA_RECORDSTORE_H diff --git a/apps/openmw/mwlua/soundbindings.cpp b/apps/openmw/mwlua/soundbindings.cpp index ad4a498153..11ad22873a 100644 --- a/apps/openmw/mwlua/soundbindings.cpp +++ b/apps/openmw/mwlua/soundbindings.cpp @@ -1,4 +1,5 @@ #include "soundbindings.hpp" +#include "recordstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -196,24 +197,7 @@ namespace MWLua }, []() { return MWBase::Environment::get().getSoundManager()->sayActive(MWWorld::ConstPtr()); }); - using SoundStore = MWWorld::Store; - sol::usertype soundStoreT = lua.new_usertype("ESM3_SoundStore"); - soundStoreT[sol::meta_function::to_string] - = [](const SoundStore& store) { return "ESM3_SoundStore{" + std::to_string(store.getSize()) + " sounds}"; }; - soundStoreT[sol::meta_function::length] = [](const SoundStore& store) { return store.getSize(); }; - soundStoreT[sol::meta_function::index] = sol::overload( - [](const SoundStore& store, size_t index) -> const ESM::Sound* { - if (index == 0 || index > store.getSize()) - return nullptr; - return store.at(index - 1); - }, - [](const SoundStore& store, std::string_view soundId) -> const ESM::Sound* { - return store.search(ESM::RefId::deserializeText(soundId)); - }); - soundStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); - soundStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); - - api["sounds"] = &MWBase::Environment::get().getWorld()->getStore().get(); + addRecordFunctionBinding(api, context); // Sound record auto soundT = lua.new_usertype("ESM3_Sound"); diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index eaa1f89d97..ad0f585207 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -22,7 +22,7 @@ #include "../mwworld/esmstore.hpp" #include "objectvariant.hpp" -#include "types/types.hpp" +#include "recordstore.hpp" namespace { diff --git a/apps/openmw/mwlua/types/types.hpp b/apps/openmw/mwlua/types/types.hpp index b52846508a..76bd2848e0 100644 --- a/apps/openmw/mwlua/types/types.hpp +++ b/apps/openmw/mwlua/types/types.hpp @@ -6,22 +6,8 @@ #include #include -#include "apps/openmw/mwbase/environment.hpp" -#include "apps/openmw/mwworld/esmstore.hpp" -#include "apps/openmw/mwworld/store.hpp" - #include "../context.hpp" -#include "../object.hpp" - -namespace sol -{ - // Ensure sol does not try to create the automatic Container or usertype bindings for Store. - // They include write operations and we want the store to be read-only. - template - struct is_automagical> : std::false_type - { - }; -} +#include "../recordstore.hpp" namespace MWLua { @@ -68,36 +54,6 @@ namespace MWLua void addESM4DoorBindings(sol::table door, const Context& context); void addESM4TerminalBindings(sol::table term, const Context& context); - - template - void addRecordFunctionBinding( - sol::table& table, const Context& context, const std::string& recordName = std::string(T::getRecordType())) - { - const MWWorld::Store& store = MWBase::Environment::get().getESMStore()->get(); - - table["record"] = sol::overload([](const Object& obj) -> const T* { return obj.ptr().get()->mBase; }, - [&store](std::string_view id) -> const T* { return store.search(ESM::RefId::deserializeText(id)); }); - - // Define a custom user type for the store. - // Provide the interface of a read-only array. - using StoreT = MWWorld::Store; - sol::state_view& lua = context.mLua->sol(); - sol::usertype storeT = lua.new_usertype(recordName + "WorldStore"); - storeT[sol::meta_function::to_string] = [recordName](const StoreT& store) { - return "{" + std::to_string(store.getSize()) + " " + recordName + " records}"; - }; - storeT[sol::meta_function::length] = [](const StoreT& store) { return store.getSize(); }; - storeT[sol::meta_function::index] = [](const StoreT& store, size_t index) -> const T* { - if (index == 0 || index > store.getSize()) - return nullptr; - return store.at(index - 1); // Translate from Lua's 1-based indexing. - }; - storeT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); - storeT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); - - // Provide access to the store. - table["records"] = &store; - } } #endif // MWLUA_TYPES_H diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 66fb817362..86f95c4079 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -10,10 +10,6 @@ -- The revision of OpenMW Lua API. It is an integer that is incremented every time the API is changed. See the actual value at the top of the page. -- @field [parent=#core] #number API_REVISION ---- --- A read-only list of all @{#FactionRecord}s in the world database. --- @field [parent=#core] #list<#FactionRecord> factions - --- -- Terminates the game and quits to the OS. Should be used only for testing purposes. -- @function [parent=#core] quit @@ -622,41 +618,52 @@ -- @field #number Curse Curse -- @field #number Power Power, can be used once a day +--- @{#Spells}: Spells +-- @field [parent=#Magic] #Spells spells --- List of all @{#Spell}s. --- @field [parent=#Magic] #list<#Spell> spells --- @usage local spell = core.magic.spells['thunder fist'] -- get by id --- @usage local spell = core.magic.spells[1] -- get by index +-- @field [parent=#Spells] #list<#Spell> records A read-only list of all @{#Spell} records in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #Spell. +-- @usage local spell = core.magic.spells.records['thunder fist'] -- get by id +-- @usage local spell = core.magic.spells.records[1] -- get by index -- @usage -- Print all powers --- for _, spell in pairs(core.magic.spells) do +-- for _, spell in pairs(core.magic.spells.records) do -- if spell.types == core.magic.SPELL_TYPE.Power then -- print(spell.name) -- end -- end +--- @{#Effects}: Magic Effects +-- @field [parent=#Magic] #Effects effects + --- Map from @{#MagicEffectId} to @{#MagicEffect} --- @field [parent=#Magic] #map<#number, #MagicEffect> effects +-- @field [parent=#Effects] #map<#number, #MagicEffect> records -- @usage -- Print all harmful effects --- for _, effect in pairs(core.magic.effects) do +-- for _, effect in pairs(core.magic.effects.records) do -- if effect.harmful then -- print(effect.name) -- end -- end -- @usage -- Look up the record of a specific effect and print its icon --- local mgef = core.magic.effects[core.magic.EFFECT_TYPE.Reflect] +-- local mgef = core.magic.effects.records[core.magic.EFFECT_TYPE.Reflect] -- print('Reflect Icon: '..tostring(mgef.icon)) ---- List of all @{#Enchantment}s. --- @field [parent=#Magic] #list<#Enchantment> enchantments --- @usage local enchantment = core.magic.enchantments['marara's boon'] -- get by id --- @usage local enchantment = core.magic.enchantments[1] -- get by index +--- @{#Enchantments}: Enchantments +-- @field [parent=#Magic] #Enchantments enchantments + +--- A read-only list of all @{#Enchantment} records in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) and [iterables#Map](iterables.html#map-iterable) of #Enchantment. +-- @field [parent=#Enchantments] #list<#Enchantment> records +-- @usage local enchantment = core.magic.enchantments.records['marara's boon'] -- get by id +-- @usage local enchantment = core.magic.enchantments.records[1] -- get by index -- @usage -- Print all enchantments with constant effect --- for _, ench in pairs(core.magic.enchantments) do +-- for _, ench in pairs(core.magic.enchantments.records) do -- if ench.type == core.magic.ENCHANTMENT_TYPE.ConstantEffect then -- print(ench.id) -- end -- end + --- -- @type Spell -- @field #string id Spell id @@ -827,11 +834,12 @@ -- @field #number maxRange Raw maximal range value, from 0 to 255 --- List of all @{#SoundRecord}s. --- @field [parent=#Sound] #list<#SoundRecord> sounds --- @usage local sound = core.sound.sounds['Ashstorm'] -- get by id --- @usage local sound = core.sound.sounds[1] -- get by index +-- @field [parent=#Sound] #list<#SoundRecord> records A read-only list of all @{#SoundRecord}s in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #SoundRecord. +-- @usage local sound = core.sound.records['Ashstorm'] -- get by id +-- @usage local sound = core.sound.records[1] -- get by index -- @usage -- Print all sound files paths --- for _, sound in pairs(core.sound.sounds) do +-- for _, sound in pairs(core.sound.records) do -- print(sound.fileName) -- end @@ -844,7 +852,10 @@ --- `core.stats.Attribute` -- @type Attribute --- @field #list<#AttributeRecord> records A read-only list of all @{#AttributeRecord}s in the world database. +-- @field #list<#AttributeRecord> records A read-only list of all @{#AttributeRecord}s in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #AttributeRecord. +-- @usage local record = core.stats.Attribute.records['example_recordid'] +-- @usage local record = core.stats.Attribute.records[1] --- -- Returns a read-only @{#AttributeRecord} @@ -857,7 +868,10 @@ --- `core.stats.Skill` -- @type Skill --- @field #list<#SkillRecord> records A read-only list of all @{#SkillRecord}s in the world database. +-- @field #list<#SkillRecord> records A read-only list of all @{#SkillRecord}s in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #SkillRecord. +-- @usage local record = core.stats.Skill.records['example_recordid'] +-- @usage local record = core.stats.Skill.records[1] --- -- Returns a read-only @{#SkillRecord} @@ -892,6 +906,15 @@ -- @field #string failureSound VFS path to the failure sound -- @field #string hitSound VFS path to the hit sound +--- @{#Factions}: Factions +-- @field [parent=#core] #Factions factions + +--- +-- A read-only list of all @{#FactionRecord}s in the world database. +-- @field [parent=#Factions] #list<#FactionRecord> records +-- @usage local record = core.factions.records['example_recordid'] +-- @usage local record = core.factions.records[1] + --- -- Faction data record -- @type FactionRecord diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 60f3e79628..32b073783b 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -718,7 +718,13 @@ -- @type Creature -- @extends #Actor -- @field #Actor baseType @{#Actor} --- @field #list<#CreatureRecord> records A read-only list of all @{#CreatureRecord}s in the world database. + +--- +-- A read-only list of all @{#CreatureRecord}s in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #CreatureRecord. +-- @field [parent=#Creature] #list<#CreatureRecord> records +-- @usage local record = types.NPC.classes['example_recordid'] +-- @usage local record = types.NPC.classes[1] --- -- Whether the object is a creature. @@ -772,7 +778,13 @@ -- @extends #Actor -- @field #Actor baseType @{#Actor} -- @field [parent=#NPC] #NpcStats stats --- @field #list<#NpcRecord> records A read-only list of all @{#NpcRecord}s in the world database. + +--- +-- A read-only list of all @{#NpcRecord}s in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #NpcRecord. +-- @field [parent=#NPC] #map<#NpcRecord> records +-- @usage local record = types.NPC.classes['example_recordid'] +-- @usage local record = types.NPC.classes[1] --- -- Whether the object is an NPC or a Player. @@ -925,8 +937,11 @@ -- @field [parent=#NPC] #Classes classes --- --- A read-only list of all @{#ClassRecord}s in the world database. +-- A read-only list of all @{#ClassRecord}s in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #ClassRecord. -- @field [parent=#Classes] #list<#ClassRecord> records +-- @usage local record = types.NPC.classes['example_recordid'] +-- @usage local record = types.NPC.classes[1] --- -- Returns a read-only @{#ClassRecord} @@ -963,7 +978,10 @@ --- -- A read-only list of all @{#RaceRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #RaceRecord. -- @field [parent=#Races] #list<#RaceRecord> records +-- @usage local record = types.NPC.classes['example_recordid'] +-- @usage local record = types.NPC.classes[1] --- -- Returns a read-only @{#RaceRecord} @@ -1120,7 +1138,10 @@ --- -- A read-only list of all @{#BirthSignRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #BirthSignRecord. -- @field [parent=#BirthSigns] #list<#BirthSignRecord> records +-- @usage local record = types.NPC.classes['example_recordid'] +-- @usage local record = types.NPC.classes[1] --- -- Returns a read-only @{#BirthSignRecord} @@ -1152,7 +1173,6 @@ -- @type Armor -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#ArmorRecord> records A read-only list of all @{#ArmorRecord}s in the world database. --- -- Whether the object is an Armor. @@ -1174,6 +1194,13 @@ -- @field #number LBracer -- @field #number RBracer +--- +-- A read-only list of all @{#ArmorRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ArmorRecord. +-- @field [parent=#Armor] #list<#ArmorRecord> records +-- @usage local record = types.Armor.records['example_recordid'] +-- @usage local record = types.Armor.records[1] + --- @{#ArmorTYPE} -- @field [parent=#Armor] #ArmorTYPE TYPE @@ -1219,7 +1246,13 @@ -- @type Book -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#BookRecord> records A read-only list of all @{#BookRecord}s in the world database. + +--- +-- A read-only list of all @{#BookRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #BookRecord. +-- @field [parent=#Book] #list<#BookRecord> records +-- @usage local record = types.Book.records['example_recordid'] +-- @usage local record = types.Book.records[1] --- -- Whether the object is a Book. @@ -1295,7 +1328,13 @@ -- @type Clothing -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#ClothingRecord> records A read-only list of all @{#ClothingRecord}s in the world database. + +--- +-- A read-only list of all @{#ClothingRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ClothingRecord. +-- @field [parent=#Clothing] #list<#ClothingRecord> records +-- @usage local record = types.Clothing.records['example_recordid'] +-- @usage local record = types.Clothing.records[1] --- -- Whether the object is a Clothing. @@ -1361,7 +1400,13 @@ -- @type Ingredient -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#IngredientRecord> records A read-only list of all @{#IngredientRecord}s in the world database. + +--- +-- A read-only list of all @{#IngredientRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #IngredientRecord. +-- @field [parent=#Ingredient] #list<#IngredientRecord> records +-- @usage local record = types.Ingredient.records['example_recordid'] +-- @usage local record = types.Ingredient.records[1] --- -- Whether the object is an Ingredient. @@ -1448,7 +1493,13 @@ -- @type Light -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#LightRecord> records A read-only list of all @{#LightRecord}s in the world database. + +--- +-- A read-only list of all @{#LightRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #LightRecord. +-- @field [parent=#Light] #list<#LightRecord> records +-- @usage local record = types.Light.records['example_recordid'] +-- @usage local record = types.Light.records[1] --- -- Whether the object is a Light. @@ -1486,7 +1537,13 @@ -- @type Miscellaneous -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#MiscellaneousRecord> records A read-only list of all @{#MiscellaneousRecord}s in the world database. + +--- +-- A read-only list of all @{#MiscellaneousRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #MiscellaneousRecord. +-- @field [parent=#Miscellaneous] #list<#MiscellaneousRecord> records +-- @usage local record = types.Miscellaneous.records['example_recordid'] +-- @usage local record = types.Miscellaneous.records[1] --- -- Whether the object is a Miscellaneous. @@ -1537,7 +1594,13 @@ -- @type Potion -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#PotionRecord> records A read-only list of all @{#PotionRecord}s in the world database. + +--- +-- A read-only list of all @{#PotionRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #PotionRecord. +-- @field [parent=#Potion] #list<#PotionRecord> records +-- @usage local record = types.Potion.records['example_recordid'] +-- @usage local record = types.Potion.records[1] --- -- Whether the object is a Potion. @@ -1578,7 +1641,13 @@ -- @type Weapon -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#WeaponRecord> records A read-only list of all @{#WeaponRecord}s in the world database. + +--- +-- A read-only list of all @{#WeaponRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #WeaponRecord. +-- @field [parent=#Weapon] #list<#WeaponRecord> records +-- @usage local record = types.Weapon.records['example_recordid'] +-- @usage local record = types.Weapon.records[1] --- -- Whether the object is a Weapon. @@ -1650,7 +1719,14 @@ -- @type Apparatus -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#ApparatusRecord> records A read-only list of all @{#ApparatusRecord}s in the world database. + + +--- +-- A read-only list of all @{#ApparatusRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ApparatusRecord. +-- @field [parent=#Apparatus] #list<#ApparatusRecord> records +-- @usage local record = types.Apparatus.records['example_recordid'] +-- @usage local record = types.Apparatus.records[1] --- -- Whether the object is an Apparatus. @@ -1693,7 +1769,13 @@ -- @type Lockpick -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#LockpickRecord> records A read-only list of all @{#LockpickRecord}s in the world database. + +--- +-- A read-only list of all @{#LockpickRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #LockpickRecord. +-- @field [parent=#Lockpick] #list<#LockpickRecord> records +-- @usage local record = types.Lockpick.records['example_recordid'] +-- @usage local record = types.Lockpick.records[1] --- -- Whether the object is a Lockpick. @@ -1726,7 +1808,13 @@ -- @type Probe -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#ProbeRecord> records A read-only list of all @{#ProbeRecord}s in the world database. + +--- +-- A read-only list of all @{#ProbeRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ProbeRecord. +-- @field [parent=#Probe] #list<#ProbeRecord> records +-- @usage local record = types.Probe.records['example_recordid'] +-- @usage local record = types.Probe.records[1] --- -- Whether the object is a Probe. @@ -1759,7 +1847,13 @@ -- @type Repair -- @extends #Item -- @field #Item baseType @{#Item} --- @field #list<#RepairRecord> records A read-only list of all @{#RepairRecord}s in the world database. + +--- +-- A read-only list of all @{#RepairRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #RepairRecord. +-- @field [parent=#Repair] #list<#RepairRecord> records +-- @usage local record = types.Repair.records['example_recordid'] +-- @usage local record = types.Repair.records[1] --- -- Whether the object is a Repair. @@ -1790,7 +1884,13 @@ --- -- @type Activator --- @field #list<#ActivatorRecord> records A read-only list of all @{#ActivatorRecord}s in the world database. + +--- +-- A read-only list of all @{#ActivatorRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ActivatorRecord. +-- @field [parent=#Activator] #list<#ActivatorRecord> records +-- @usage local record = types.Activator.records['example_recordid'] +-- @usage local record = types.Activator.records[1] --- -- Whether the object is an Activator. @@ -1827,7 +1927,13 @@ -- @type Container -- @extends #Lockable -- @field #Lockable baseType @{#Lockable} --- @field #list<#ContainerRecord> records A read-only list of all @{#ContainerRecord}s in the world database. + +--- +-- A read-only list of all @{#ContainerRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ContainerRecord. +-- @field [parent=#Container] #list<#ContainerRecord> records +-- @usage local record = types.Container.records['example_recordid'] +-- @usage local record = types.Container.records[1] --- -- Container content. @@ -1882,7 +1988,13 @@ -- @type Door -- @extends #Lockable -- @field #Lockable baseType @{#Lockable} --- @field #list<#DoorRecord> records A read-only list of all @{#DoorRecord}s in the world database. + +--- +-- A read-only list of all @{#DoorRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #DoorRecord. +-- @field [parent=#Door] #list<#DoorRecord> records +-- @usage local record = types.Door.records['example_recordid'] +-- @usage local record = types.Door.records[1] --- -- Whether the object is a Door. @@ -1936,7 +2048,13 @@ --- -- @type Static --- @field #list<#StaticRecord> records A read-only list of all @{#StaticRecord}s in the world database. + +--- +-- A read-only list of all @{#StaticRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #StaticRecord. +-- @field [parent=#Static] #list<#StaticRecord> records +-- @usage local record = types.Static.records['example_recordid'] +-- @usage local record = types.Static.records[1] --- -- Whether the object is a Static. @@ -1961,7 +2079,13 @@ --- -- @type CreatureLevelledList --- @field #list<#CreatureLevelledListRecord> records A read-only list of all @{#CreatureLevelledListRecord}s in the world database. + +--- +-- A read-only list of all @{#CreatureLevelledListRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #CreatureLevelledListRecord. +-- @field [parent=#CreatureLevelledList] #list<#CreatureLevelledListRecord> records +-- @usage local record = types.CreatureLevelledList.records['example_recordid'] +-- @usage local record = types.CreatureLevelledList.records[1] --- -- Whether the object is a CreatureLevelledList. @@ -2045,7 +2169,13 @@ --- -- @type ESM4Terminal --- @field #list<#ESM4TerminalRecord> records A read-only list of all @{#ESM4TerminalRecord}s in the world database. + +--- +-- A read-only list of all @{#ESM4TerminalRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ESM4TerminalRecord. +-- @field [parent=#ESM4Terminal] #list<#ESM4TerminalRecord> records +-- @usage local record = types.ESM4Terminal.records['example_recordid'] +-- @usage local record = types.ESM4Terminal.records[1] --- -- Whether the object is a ESM4Terminal. @@ -2110,9 +2240,11 @@ -- @return #ESM4DoorRecord --- --- Returns a read-only list of all @{#ESM4DoorRecord}s in the world database. --- @function [parent=#ESM4Door] records --- @return #list<#ESM4DoorRecord> +-- A read-only list of all @{#ESM4DoorRecord}s in the world database. +-- Implements [iterables#List](iterables.html#List) of #ESM4DoorRecord. +-- @field [parent=#ESM4Door] #list<#ESM4DoorRecord> records +-- @usage local record = types.ESM4Door.records['example_recordid'] +-- @usage local record = types.ESM4Door.records[1] --- -- @type ESM4DoorRecord diff --git a/scripts/data/integration_tests/test_lua_api/test.lua b/scripts/data/integration_tests/test_lua_api/test.lua index 53262dd168..863cdd0f57 100644 --- a/scripts/data/integration_tests/test_lua_api/test.lua +++ b/scripts/data/integration_tests/test_lua_api/test.lua @@ -2,6 +2,7 @@ local testing = require('testing_util') local core = require('openmw.core') local async = require('openmw.async') local util = require('openmw.util') +local types = require('openmw.types') local world = require('openmw.world') local function testTimers() @@ -87,6 +88,45 @@ local function testMWScript() testing.expectEqual(variableStoreCount, indexCheck) end +local function testRecordStore(store,storeName,skipPairs) + testing.expect(store.records) + local firstRecord = store.records[1] + if not firstRecord then return end + testing.expectEqual(firstRecord.id,store.records[firstRecord.id].id) + local status, _ = pcall(function() + for index, value in ipairs(store.records) do + if value.id == firstRecord.id then + testing.expectEqual(index,1,storeName) + break + end + end + end) + + testing.expectEqual(status,true,storeName) + +end + +local function testRecordStores() + for key, type in pairs(types) do + if type.records then + testRecordStore(type,key) + end + end + testRecordStore(core.magic.enchantments,"enchantments") + testRecordStore(core.magic.effects,"effects",true) + testRecordStore(core.magic.spells,"spells") + + testRecordStore(core.stats.Attribute,"Attribute") + testRecordStore(core.stats.Skill,"Skill") + + testRecordStore(core.sound,"sound") + testRecordStore(core.factions,"factions") + + testRecordStore(types.NPC.classes,"classes") + testRecordStore(types.NPC.races,"races") + testRecordStore(types.Player.birthSigns,"birthSigns") +end + local function initPlayer() player:teleport('', util.vector3(4096, 4096, 867.237), util.transform.identity) coroutine.yield() @@ -124,6 +164,7 @@ tests = { end}, {'teleport', testTeleport}, {'getGMST', testGetGMST}, + {'recordStores', testRecordStores}, {'mwscript', testMWScript}, }