diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 078ccc448d..6de44bf036 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -62,7 +62,7 @@ add_openmw_dir (mwlua luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant context globalscripts localscripts playerscripts luabindings objectbindings cellbindings camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings - types/types types/door types/actor types/container 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/types types/door types/actor types/container 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 worker magicbindings ) diff --git a/apps/openmw/mwlua/types/levelledlist.cpp b/apps/openmw/mwlua/types/levelledlist.cpp new file mode 100644 index 0000000000..cb3bd7a6bc --- /dev/null +++ b/apps/openmw/mwlua/types/levelledlist.cpp @@ -0,0 +1,59 @@ +#include "types.hpp" + +#include + +#include "../../mwbase/environment.hpp" +#include "../../mwbase/world.hpp" +#include "../../mwmechanics/levelledlist.hpp" + +namespace sol +{ + template <> + struct is_automagical : std::false_type + { + }; + template <> + struct is_automagical : std::false_type + { + }; +} + +namespace MWLua +{ + void addLevelledCreatureBindings(sol::table list, const Context& context) + { + auto& state = context.mLua->sol(); + auto item = state.new_usertype("ESM3_LevelledListItem"); + item["id"] = sol::readonly_property( + [](const ESM::LevelledListBase::LevelItem& rec) -> std::string { return rec.mId.serializeText(); }); + item["level"] + = sol::readonly_property([](const ESM::LevelledListBase::LevelItem& rec) -> int { return rec.mLevel; }); + item[sol::meta_function::to_string] = [](const ESM::LevelledListBase::LevelItem& rec) -> std::string { + return "ESM3_LevelledListItem[" + rec.mId.toDebugString() + ", " + std::to_string(rec.mLevel) + "]"; + }; + + addRecordFunctionBinding(list, context); + + auto record = state.new_usertype("ESM3_CreatureLevelledList"); + record[sol::meta_function::to_string] = [](const ESM::CreatureLevList& rec) -> std::string { + return "ESM3_CreatureLevelledList[" + rec.mId.toDebugString() + "]"; + }; + record["id"] = sol::readonly_property( + [](const ESM::CreatureLevList& rec) -> std::string { return rec.mId.serializeText(); }); + record["chanceNone"] = sol::readonly_property( + [](const ESM::CreatureLevList& rec) -> float { return std::clamp(rec.mChanceNone / 100.f, 0.f, 1.f); }); + record["creatures"] = sol::readonly_property([&](const ESM::CreatureLevList& rec) -> sol::table { + sol::table res(state, sol::create); + for (size_t i = 0; i < rec.mList.size(); ++i) + res[i + 1] = rec.mList[i]; + return res; + }); + record["calculateFromAllLevels"] = sol::readonly_property( + [](const ESM::CreatureLevList& rec) -> bool { return rec.mFlags & ESM::CreatureLevList::AllLevels; }); + + record["getRandomId"] = [](const ESM::CreatureLevList& rec, int level) -> std::string { + auto& prng = MWBase::Environment::get().getWorld()->getPrng(); + return MWMechanics::getLevelledItem(&rec, true, prng, level).serializeText(); + }; + } +} diff --git a/apps/openmw/mwlua/types/types.cpp b/apps/openmw/mwlua/types/types.cpp index a3e0b52cbd..8fed1e1a24 100644 --- a/apps/openmw/mwlua/types/types.cpp +++ b/apps/openmw/mwlua/types/types.cpp @@ -20,6 +20,7 @@ namespace MWLua constexpr std::string_view Creature = "Creature"; constexpr std::string_view Door = "Door"; constexpr std::string_view Ingredient = "Ingredient"; + constexpr std::string_view LevelledCreature = "LevelledCreature"; constexpr std::string_view Light = "Light"; constexpr std::string_view MiscItem = "Miscellaneous"; constexpr std::string_view NPC = "NPC"; @@ -63,6 +64,7 @@ namespace MWLua { ESM::REC_CREA, ObjectTypeName::Creature }, { ESM::REC_DOOR, ObjectTypeName::Door }, { ESM::REC_INGR, ObjectTypeName::Ingredient }, + { ESM::REC_LEVC, ObjectTypeName::LevelledCreature }, { ESM::REC_LIGH, ObjectTypeName::Light }, { ESM::REC_MISC, ObjectTypeName::MiscItem }, { ESM::REC_NPC_, ObjectTypeName::NPC }, @@ -189,6 +191,8 @@ namespace MWLua addType(ObjectTypeName::NPC, { ESM::REC_INTERNAL_PLAYER, ESM::REC_NPC_ }, ObjectTypeName::Actor), context); addPlayerBindings(addType(ObjectTypeName::Player, { ESM::REC_INTERNAL_PLAYER }, ObjectTypeName::NPC), context); + addLevelledCreatureBindings(addType(ObjectTypeName::LevelledCreature, { ESM::REC_LEVC }), context); + addArmorBindings(addType(ObjectTypeName::Armor, { ESM::REC_ARMO }, ObjectTypeName::Item), context); addClothingBindings(addType(ObjectTypeName::Clothing, { ESM::REC_CLOT }, ObjectTypeName::Item), context); addIngredientBindings(addType(ObjectTypeName::Ingredient, { ESM::REC_INGR }, ObjectTypeName::Item), context); diff --git a/apps/openmw/mwlua/types/types.hpp b/apps/openmw/mwlua/types/types.hpp index abd2cb0051..7152fc5711 100644 --- a/apps/openmw/mwlua/types/types.hpp +++ b/apps/openmw/mwlua/types/types.hpp @@ -63,6 +63,7 @@ namespace MWLua void addClothingBindings(sol::table clothing, const Context& context); void addStaticBindings(sol::table stat, const Context& context); void addLightBindings(sol::table light, const Context& context); + void addLevelledCreatureBindings(sol::table list, const Context& context); void addESM4DoorBindings(sol::table door, const Context& context); diff --git a/apps/openmw/mwmechanics/levelledlist.cpp b/apps/openmw/mwmechanics/levelledlist.cpp index 795c641391..26f759425c 100644 --- a/apps/openmw/mwmechanics/levelledlist.cpp +++ b/apps/openmw/mwmechanics/levelledlist.cpp @@ -15,12 +15,20 @@ namespace MWMechanics { - ESM::RefId getLevelledItem(const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Generator& prng) + ESM::RefId getLevelledItem( + const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Generator& prng, std::optional level) { const std::vector& items = levItem->mList; - const MWWorld::Ptr& player = getPlayer(); - int playerLevel = player.getClass().getCreatureStats(player).getLevel(); + int playerLevel; + if (level.has_value()) + playerLevel = *level; + else + { + const MWWorld::Ptr& player = getPlayer(); + playerLevel = player.getClass().getCreatureStats(player).getLevel(); + level = playerLevel; + } if (Misc::Rng::roll0to99(prng) < levItem->mChanceNone) return ESM::RefId(); @@ -65,9 +73,9 @@ namespace MWMechanics else { if (ref.getPtr().getType() == ESM::ItemLevList::sRecordId) - return getLevelledItem(ref.getPtr().get()->mBase, false, prng); + return getLevelledItem(ref.getPtr().get()->mBase, false, prng, level); else - return getLevelledItem(ref.getPtr().get()->mBase, true, prng); + return getLevelledItem(ref.getPtr().get()->mBase, true, prng, level); } } } diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index 3348118366..5a739dd413 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -3,6 +3,8 @@ #include +#include + namespace ESM { struct LevelledListBase; @@ -13,7 +15,8 @@ namespace MWMechanics { /// @return ID of resulting item, or empty if none - ESM::RefId getLevelledItem(const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Generator& prng); + ESM::RefId getLevelledItem( + const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Generator& prng, std::optional level = {}); } diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index ccb7c38897..8b86e7f816 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -1382,6 +1382,46 @@ -- @field #string id Record id -- @field #string model VFS path to the model + +--- @{#CreatureLevelledList} functions +-- @field [parent=#types] #CreatureLevelledList CreatureLevelledList + +--- +-- @type CreatureLevelledList +-- @field #list<#CreatureLevelledListRecord> records A read-only list of all @{#CreatureLevelledListRecord}s in the world database. + +--- +-- Whether the object is a CreatureLevelledList. +-- @function [parent=#CreatureLevelledList] objectIsInstance +-- @param openmw.core#GameObject object +-- @return #boolean + +--- +-- Returns the read-only @{#CreatureLevelledListRecord} of a levelled creature +-- @function [parent=#CreatureLevelledList] record +-- @param #any objectOrRecordId +-- @return #CreatureLevelledListRecord + +--- +-- @type CreatureLevelledListRecord +-- @field #string id Record id +-- @field #number chanceNone Chance this list won't spawn anything [0-1] +-- @field #boolean calculateFromAllLevels Calculate from all levels <= player level, not just the closest below player +-- @field #list<#LevelledListItem> creatures + +--- +-- Picks a random id from the levelled list. +-- @function [parent=#CreatureLevelledListRecord] getRandomId +-- @param openmw.core#CreatureLevelledListRecord The list +-- @param #number The maximum level to select entries for +-- @return #string An id + +--- +-- @type LevelledListItem +-- @field #string id Item id +-- @field #number level The minimum player level at which this item can occur + + --- Functions for @{#ESM4Activator} objects -- @field [parent=#types] #ESM4Activator ESM4Activator