diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 6de44bf036..d527b73a90 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/levelledlist + types/types types/door 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 types/clothing types/levelledlist worker magicbindings ) diff --git a/apps/openmw/mwlua/types/lockable.cpp b/apps/openmw/mwlua/types/lockable.cpp new file mode 100644 index 0000000000..2a400f3ccf --- /dev/null +++ b/apps/openmw/mwlua/types/lockable.cpp @@ -0,0 +1,87 @@ + +#include "types.hpp" +#include +#include +#include +#include + +#include + +#include "../luabindings.hpp" +#include "../worldview.hpp" + +namespace MWLua +{ + + void addLockableBindings(sol::table lockable) + { + lockable["getLockLevel"] + = [](const Object& object) { return std::abs(object.ptr().getCellRef().getLockLevel()); }; + lockable["isLocked"] = [](const Object& object) { return object.ptr().getCellRef().isLocked(); }; + lockable["getKeyRecord"] = [](const Object& object) -> sol::optional { + ESM::RefId key = object.ptr().getCellRef().getKey(); + if (key.empty()) + return sol::nullopt; + return MWBase::Environment::get().getESMStore()->get().find(key); + }; + lockable["lock"] = [](const GObject& object, sol::optional lockLevel) { + object.ptr().getCellRef().setLocked(true); + + int level = 1; + + if (lockLevel) + level = lockLevel.value(); + else if (object.ptr().getCellRef().getLockLevel() < 0) + level = -object.ptr().getCellRef().getLockLevel(); + else if (object.ptr().getCellRef().getLockLevel() > 0) + level = object.ptr().getCellRef().getLockLevel(); + + object.ptr().getCellRef().setLockLevel(level); + }; + lockable["unlock"] = [](const GObject& object) { + if (!object.ptr().getCellRef().isLocked()) + return; + object.ptr().getCellRef().setLocked(false); + + object.ptr().getCellRef().setLockLevel(-object.ptr().getCellRef().getLockLevel()); + }; + lockable["setTrapSpell"] = [](const GObject& object, const sol::object& spellOrId) { + if (spellOrId == sol::nil) + { + object.ptr().getCellRef().setTrap(ESM::RefId()); // remove the trap value + return; + } + if (spellOrId.is()) + object.ptr().getCellRef().setTrap(spellOrId.as()->mId); + else + { + ESM::RefId spellId = ESM::RefId::deserializeText(LuaUtil::cast(spellOrId)); + const auto& spellStore = MWBase::Environment::get().getESMStore()->get(); + const ESM::Spell* spell = spellStore.find(spellId); + object.ptr().getCellRef().setTrap(spell->mId); + } + }; + lockable["setKeyRecord"] = [](const GObject& object, const sol::object& itemOrRecordId) { + if (itemOrRecordId == sol::nil) + { + object.ptr().getCellRef().setKey(ESM::RefId()); // remove the trap value + return; + } + if (itemOrRecordId.is()) + object.ptr().getCellRef().setKey(itemOrRecordId.as()->mId); + else + { + ESM::RefId miscId = ESM::RefId::deserializeText(LuaUtil::cast(itemOrRecordId)); + const auto& keyStore = MWBase::Environment::get().getESMStore()->get(); + const ESM::Miscellaneous* key = keyStore.find(miscId); + object.ptr().getCellRef().setKey(key->mId); + } + }; + lockable["getTrapSpell"] = [](sol::this_state lua, const Object& o) -> sol::optional { + ESM::RefId trap = o.ptr().getCellRef().getTrap(); + if (trap.empty()) + return sol::nullopt; + return MWBase::Environment::get().getESMStore()->get().find(trap); + }; + } +} diff --git a/apps/openmw/mwlua/types/types.cpp b/apps/openmw/mwlua/types/types.cpp index 8fed1e1a24..053c882a3f 100644 --- a/apps/openmw/mwlua/types/types.cpp +++ b/apps/openmw/mwlua/types/types.cpp @@ -11,6 +11,7 @@ namespace MWLua // These names are part of OpenMW Lua API. constexpr std::string_view Actor = "Actor"; // base type for NPC, Creature, Player constexpr std::string_view Item = "Item"; // base type for all items + constexpr std::string_view Lockable = "Lockable"; // base type for doors and containers constexpr std::string_view Activator = "Activator"; constexpr std::string_view Armor = "Armor"; @@ -185,6 +186,8 @@ namespace MWLua addType(ObjectTypeName::Item, { ESM::REC_ARMO, ESM::REC_BOOK, ESM::REC_CLOT, ESM::REC_INGR, ESM::REC_LIGH, ESM::REC_MISC, ESM::REC_ALCH, ESM::REC_WEAP, ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA }); + addLockableBindings( + addType(ObjectTypeName::Lockable, { ESM::REC_CONT, ESM::REC_DOOR, ESM::REC_CONT4, ESM::REC_DOOR4 })); addCreatureBindings(addType(ObjectTypeName::Creature, { ESM::REC_CREA }, ObjectTypeName::Actor), context); addNpcBindings( @@ -207,8 +210,8 @@ namespace MWLua addRepairBindings(addType(ObjectTypeName::Repair, { ESM::REC_REPA }, ObjectTypeName::Item), context); addActivatorBindings(addType(ObjectTypeName::Activator, { ESM::REC_ACTI }), context); - addContainerBindings(addType(ObjectTypeName::Container, { ESM::REC_CONT }), context); - addDoorBindings(addType(ObjectTypeName::Door, { ESM::REC_DOOR }), context); + addContainerBindings(addType(ObjectTypeName::Container, { ESM::REC_CONT }, ObjectTypeName::Lockable), context); + addDoorBindings(addType(ObjectTypeName::Door, { ESM::REC_DOOR }, ObjectTypeName::Lockable), context); addStaticBindings(addType(ObjectTypeName::Static, { ESM::REC_STAT }), context); addType(ObjectTypeName::ESM4Activator, { ESM::REC_ACTI4 }); @@ -217,7 +220,7 @@ namespace MWLua addType(ObjectTypeName::ESM4Book, { ESM::REC_BOOK4 }); addType(ObjectTypeName::ESM4Clothing, { ESM::REC_CLOT4 }); addType(ObjectTypeName::ESM4Container, { ESM::REC_CONT4 }); - addESM4DoorBindings(addType(ObjectTypeName::ESM4Door, { ESM::REC_DOOR4 }), context); + addESM4DoorBindings(addType(ObjectTypeName::ESM4Door, { ESM::REC_DOOR4 }, ObjectTypeName::Lockable), context); addType(ObjectTypeName::ESM4Furniture, { ESM::REC_FURN4 }); addType(ObjectTypeName::ESM4Ingredient, { ESM::REC_INGR4 }); addType(ObjectTypeName::ESM4Light, { ESM::REC_LIGH4 }); diff --git a/apps/openmw/mwlua/types/types.hpp b/apps/openmw/mwlua/types/types.hpp index 7152fc5711..34b4991ac5 100644 --- a/apps/openmw/mwlua/types/types.hpp +++ b/apps/openmw/mwlua/types/types.hpp @@ -60,6 +60,7 @@ namespace MWLua void addPotionBindings(sol::table potion, const Context& context); void addIngredientBindings(sol::table Ingredient, const Context& context); void addArmorBindings(sol::table armor, const Context& context); + void addLockableBindings(sol::table lockable); void addClothingBindings(sol::table clothing, const Context& context); void addStaticBindings(sol::table stat, const Context& context); void addLightBindings(sol::table light, const Context& context); diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 76f979b693..3f85f9c9ee 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -316,6 +316,19 @@ namespace MWWorld } } + void CellRef::setKey(const ESM::RefId& key) + { + if (key != getKey()) + { + mChanged = true; + std::visit(ESM::VisitOverload{ + [&](ESM4::Reference& /*ref*/) {}, + [&](ESM::CellRef& ref) { ref.mKey = key; }, + }, + mCellRef.mVariant); + } + } + void CellRef::setGoldValue(int value) { if (value != getGoldValue()) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 24f2bd51f8..56e3f094ba 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -184,7 +184,7 @@ namespace MWWorld { return std::visit([](auto&& ref) -> const ESM::RefId& { return ref.mKey; }, mCellRef.mVariant); } - + void setKey(const ESM::RefId& key); ESM::RefId getTrap() const { struct Visitor diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 99d7387947..268175316b 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -893,6 +893,59 @@ -- @field #number value +--- @{#Lockable} functions +-- @field [parent=#types] #Lockable Lockable + +--- +-- Returns the key record of a lockable object(door, container) +-- @function [parent=#Lockable] getKeyRecord +-- @param openmw.core#GameObject object +-- @return #MiscellaneousRecord + +--- +-- Sets the key of a lockable object(door, container); removes it if empty string is provided. Must be used in a global script. +-- @function [parent=#Lockable] setKeyRecord +-- @param openmw.core#GameObject object +-- @param #any miscOrId @{#MiscellaneousRecord} or string misc item id Record ID of the key to use. + +--- +-- Returns the trap spell of a lockable object(door, container) +-- @function [parent=#Lockable] getTrapSpell +-- @param openmw.core#GameObject object +-- @return openmw.core#Spell + +--- +-- Sets the trap spell of a lockable object(door, container); removes it if empty string is provided. Must be used in a global script. +-- @function [parent=#Lockable] setTrapSpell +-- @param openmw.core#GameObject object +-- @param #any spellOrId @{openmw.core#Spell} or string spell id Record ID for the trap to use + +--- +-- Returns the lock level of a lockable object(door, container). Does not determine if an object is locked or not, if an object is locked while this is set above 0, this value will be used if no other value is specified. +-- @function [parent=#Lockable] getLockLevel +-- @param openmw.core#GameObject object +-- @return #number + + +--- +-- Returns true if the lockable object is locked, and false if it is not. +-- @function [parent=#Lockable] isLocked +-- @param openmw.core#GameObject object +-- @return #boolean + + +--- +-- Sets the lock level level of a lockable object(door, container);Locks if not already locked; Must be used in a global script. +-- @function [parent=#Lockable] lock +-- @param openmw.core#GameObject object +-- @param #number lockLevel Level to lock the object at. Optional, if not specified, then 1 will be used, or the previous level if it was locked before. + +--- +-- Unlocks the lockable object. Does not change the lock level, it can be kept for future use. +-- @function [parent=#Lockable] unlock +-- @param openmw.core#GameObject object + + --- @{#Light} functions -- @field [parent=#types] #Light Light @@ -1275,6 +1328,8 @@ --- -- @type Container +-- @extends #Lockable +-- @field #Lockable baseType @{#Lockable} -- @field #list<#ContainerRecord> records A read-only list of all @{#ContainerRecord}s in the world database. --- @@ -1320,6 +1375,8 @@ --- -- @type Door +-- @extends #Lockable +-- @field #Lockable baseType @{#Lockable} -- @field #list<#DoorRecord> records A read-only list of all @{#DoorRecord}s in the world database. --- @@ -1471,6 +1528,8 @@ --- -- @type ESM4Door +-- @extends #Lockable +-- @field #Lockable baseType @{#Lockable} --- -- Whether the object is a ESM4Door.