diff --git a/apps/openmw/mwlua/types/door.cpp b/apps/openmw/mwlua/types/door.cpp index 7dee2d7af3..ee6f99078f 100644 --- a/apps/openmw/mwlua/types/door.cpp +++ b/apps/openmw/mwlua/types/door.cpp @@ -1,6 +1,7 @@ #include "types.hpp" #include +#include #include #include @@ -15,6 +16,11 @@ namespace sol struct is_automagical : std::false_type { }; + + template <> + struct is_automagical : std::false_type + { + }; } namespace MWLua @@ -25,6 +31,11 @@ namespace MWLua return verifyType(ESM::REC_DOOR, o.ptr()); } + static const MWWorld::Ptr& door4Ptr(const Object& o) + { + return verifyType(ESM::REC_DOOR4, o.ptr()); + } + void addDoorBindings(sol::table door, const Context& context) { door["isTeleport"] = [](const Object& o) { return doorPtr(o).getCellRef().getTeleport(); }; @@ -62,4 +73,34 @@ namespace MWLua [](const ESM::Door& rec) -> std::string { return rec.mCloseSound.serializeText(); }); } + void addESM4DoorBindings(sol::table door, const Context& context) + { + door["isTeleport"] = [](const Object& o) { return door4Ptr(o).getCellRef().getTeleport(); }; + door["destPosition"] + = [](const Object& o) -> osg::Vec3f { return door4Ptr(o).getCellRef().getDoorDest().asVec3(); }; + door["destRotation"] + = [](const Object& o) -> osg::Vec3f { return door4Ptr(o).getCellRef().getDoorDest().asRotationVec3(); }; + door["destCell"] = [](sol::this_state lua, const Object& o) -> sol::object { + const MWWorld::CellRef& cellRef = door4Ptr(o).getCellRef(); + if (!cellRef.getTeleport()) + return sol::nil; + MWWorld::CellStore* cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell()); + assert(cell); + return o.getCell(lua, cell); + }; + + auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); + + addRecordFunctionBinding(door, context, "ESM4Door"); + + sol::usertype record = context.mLua->sol().new_usertype("ESM4_Door"); + record[sol::meta_function::to_string] + = [](const ESM4::Door& rec) -> std::string { return "ESM4_Door[" + rec.mId.toDebugString() + "]"; }; + record["id"] + = sol::readonly_property([](const ESM4::Door& rec) -> std::string { return rec.mId.serializeText(); }); + record["name"] = sol::readonly_property([](const ESM4::Door& rec) -> std::string { return rec.mFullName; }); + record["model"] = sol::readonly_property([vfs](const ESM4::Door& rec) -> std::string { + return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs); + }); + } } diff --git a/apps/openmw/mwlua/types/types.cpp b/apps/openmw/mwlua/types/types.cpp index 8b5fdba713..5b1d6c5278 100644 --- a/apps/openmw/mwlua/types/types.cpp +++ b/apps/openmw/mwlua/types/types.cpp @@ -32,6 +32,20 @@ namespace MWLua constexpr std::string_view Probe = "Probe"; constexpr std::string_view Repair = "Repair"; constexpr std::string_view Marker = "Marker"; + + constexpr std::string_view ESM4Activator = "ESM4Activator"; + constexpr std::string_view ESM4Ammunition = "ESM4Ammunition"; + constexpr std::string_view ESM4Armor = "ESM4Armor"; + constexpr std::string_view ESM4Book = "ESM4Book"; + constexpr std::string_view ESM4Clothing = "ESM4Clothing"; + constexpr std::string_view ESM4Container = "ESM4Container"; + constexpr std::string_view ESM4Door = "ESM4Door"; + constexpr std::string_view ESM4Ingredient = "ESM4Ingredient"; + constexpr std::string_view ESM4Light = "ESM4Light"; + constexpr std::string_view ESM4MiscItem = "ESM4Miscellaneous"; + constexpr std::string_view ESM4Potion = "ESM4Potion"; + constexpr std::string_view ESM4Static = "ESM4Static"; + constexpr std::string_view ESM4Weapon = "ESM4Weapon"; } namespace @@ -57,6 +71,20 @@ namespace MWLua { ESM::REC_LOCK, ObjectTypeName::Lockpick }, { ESM::REC_PROB, ObjectTypeName::Probe }, { ESM::REC_REPA, ObjectTypeName::Repair }, + + { ESM::REC_ACTI4, ObjectTypeName::ESM4Activator }, + { ESM::REC_AMMO4, ObjectTypeName::ESM4Ammunition }, + { ESM::REC_ARMO4, ObjectTypeName::ESM4Armor }, + { ESM::REC_BOOK4, ObjectTypeName::ESM4Book }, + { ESM::REC_CLOT4, ObjectTypeName::ESM4Clothing }, + { ESM::REC_CONT4, ObjectTypeName::ESM4Container }, + { ESM::REC_DOOR4, ObjectTypeName::ESM4Door }, + { ESM::REC_INGR4, ObjectTypeName::ESM4Ingredient }, + { ESM::REC_LIGH4, ObjectTypeName::ESM4Light }, + { ESM::REC_MISC4, ObjectTypeName::ESM4MiscItem }, + { ESM::REC_ALCH4, ObjectTypeName::ESM4Potion }, + { ESM::REC_STAT4, ObjectTypeName::ESM4Static }, + { ESM::REC_WEAP4, ObjectTypeName::ESM4Weapon }, }; } @@ -175,6 +203,20 @@ namespace MWLua addDoorBindings(addType(ObjectTypeName::Door, { ESM::REC_DOOR }), context); addStaticBindings(addType(ObjectTypeName::Static, { ESM::REC_STAT }), context); + addType(ObjectTypeName::ESM4Activator, { ESM::REC_ACTI4 }); + addType(ObjectTypeName::ESM4Ammunition, { ESM::REC_AMMO4 }); + addType(ObjectTypeName::ESM4Armor, { ESM::REC_ARMO4 }); + 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); + addType(ObjectTypeName::ESM4Ingredient, { ESM::REC_INGR4 }); + addType(ObjectTypeName::ESM4Light, { ESM::REC_LIGH4 }); + addType(ObjectTypeName::ESM4MiscItem, { ESM::REC_MISC4 }); + addType(ObjectTypeName::ESM4Potion, { ESM::REC_ALCH4 }); + addType(ObjectTypeName::ESM4Static, { ESM::REC_STAT4 }); + addType(ObjectTypeName::ESM4Weapon, { ESM::REC_WEAP4 }); + sol::table typeToPackage = getTypeToPackageTable(context.mLua->sol()); sol::table packageToType = getPackageToTypeTable(context.mLua->sol()); for (const auto& [type, name] : luaObjectTypeInfo) diff --git a/apps/openmw/mwlua/types/types.hpp b/apps/openmw/mwlua/types/types.hpp index c8546a699f..fd36db8c74 100644 --- a/apps/openmw/mwlua/types/types.hpp +++ b/apps/openmw/mwlua/types/types.hpp @@ -63,8 +63,11 @@ namespace MWLua void addStaticBindings(sol::table stat, const Context& context); void addLightBindings(sol::table light, const Context& context); + void addESM4DoorBindings(sol::table door, const Context& context); + template - void addRecordFunctionBinding(sol::table table, const Context& context) + void addRecordFunctionBinding( + sol::table table, const Context& context, const std::string& recordName = std::string(T::getRecordType())) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); @@ -75,9 +78,9 @@ namespace MWLua // 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(std::string(T::getRecordType()) + "WorldStore"); - storeT[sol::meta_function::to_string] = [](const StoreT& store) { - return "{" + std::to_string(store.getSize()) + " " + std::string(T::getRecordType()) + " records}"; + 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* { diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 7442dfe6a4..5887e5405a 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -3,10 +3,15 @@ #include #include +#include #include #include +#include +#include +#include #include +#include namespace MWWorld { @@ -90,13 +95,18 @@ namespace MWWorld return ESM::RefId::esm3ExteriorCell(index.x(), index.y()); } }; + auto esm4Visit = [&](const ESM4::Reference& ref) -> ESM::RefId { + if (ref.mDoor.destDoor.isZeroOrUnset()) + return ESM::RefId::sEmpty; + const ESM4::Reference* refDest + = MWBase::Environment::get().getWorld()->getStore().get().searchStatic( + ESM::FormIdRefId(ref.mDoor.destDoor)); + if (refDest) + return refDest->mParent; + return ESM::RefId::sEmpty; + }; - return std::visit( - ESM::VisitOverload{ - [&](const ESM4::Reference& ref) -> ESM::RefId { return ESM::RefId::sEmpty; }, - esm3Visit, - }, - mCellRef.mVariant); + return std::visit(ESM::VisitOverload{ esm3Visit, esm4Visit }, mCellRef.mVariant); } void CellRef::setScale(float scale) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 7cc60832f7..8e386b718f 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -55,7 +55,7 @@ namespace MWWorld struct Visitor { bool operator()(const ESM::CellRef& ref) { return ref.mTeleport; } - bool operator()(const ESM4::Reference& ref) { return 0; } + bool operator()(const ESM4::Reference& ref) { return !ref.mDoor.destDoor.isZeroOrUnset(); } }; return std::visit(Visitor(), mCellRef.mVariant); } diff --git a/components/esm/formid.hpp b/components/esm/formid.hpp index ba25a0fa26..9ddee5aca0 100644 --- a/components/esm/formid.hpp +++ b/components/esm/formid.hpp @@ -18,8 +18,8 @@ namespace ESM bool hasContentFile() const { return mContentFile >= 0; } bool isSet() const { return mIndex != 0 || mContentFile != -1; } - // Used in ESM4 as a null reference - bool isZero() const { return mIndex == 0 && mContentFile == 0; } + // Zero is used in ESM4 as a null reference + bool isZeroOrUnset() const { return mIndex == 0 && (mContentFile == 0 || mContentFile == -1); } std::string toString() const; FormId32 toUint32() const; diff --git a/components/esm4/loadland.cpp b/components/esm4/loadland.cpp index 201fd7a96f..732b46f00a 100644 --- a/components/esm4/loadland.cpp +++ b/components/esm4/loadland.cpp @@ -213,7 +213,7 @@ void ESM4::Land::load(ESM4::Reader& reader) bool missing = false; for (int i = 0; i < 4; ++i) { - if (mTextures[i].base.formId.isZero()) + if (mTextures[i].base.formId.isZeroOrUnset()) { // std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " missing base, quad " << i << std::endl; // std::cout << "layers " << mTextures[i].layers.size() << std::endl; diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 3c4e95f88b..bd1787fb83 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -1223,4 +1223,90 @@ -- @field #string id Record id -- @field #string model VFS path to the model +--- Functions for @{#ESM4Activator} objects +-- @field [parent=#types] #ESM4Activator ESM4Activator + +--- Functions for @{#ESM4Ammunition} objects +-- @field [parent=#types] #ESM4Ammunition ESM4Ammunition + +--- Functions for @{#ESM4Armor} objects +-- @field [parent=#types] #ESM4Armor ESM4Armor + +--- Functions for @{#ESM4Book} objects +-- @field [parent=#types] #ESM4Book ESM4Book + +--- Functions for @{#ESM4Clothing} objects +-- @field [parent=#types] #ESM4Clothing ESM4Clothing + +--- Functions for @{#ESM4Door} objects +-- @field [parent=#types] #ESM4Door ESM4Door + +--- Functions for @{#ESM4Ingredient} objects +-- @field [parent=#types] #ESM4Ingredient ESM4Ingredient + +--- Functions for @{#ESM4Light} objects +-- @field [parent=#types] #ESM4Light ESM4Light + +--- Functions for @{#ESM4Miscellaneous} objects +-- @field [parent=#types] #ESM4Miscellaneous ESM4Miscellaneous + +--- Functions for @{#ESM4Potion} objects +-- @field [parent=#types] #ESM4Potion ESM4Potion + +--- Functions for @{#ESM4Static} objects +-- @field [parent=#types] #ESM4Static ESM4Static + +--- Functions for @{#ESM4Weapon} objects +-- @field [parent=#types] #ESM4Weapon ESM4Weapon + +--- +-- @type ESM4Door + +--- +-- Whether the object is a ESM4Door. +-- @function [parent=#ESM4Door] objectIsInstance +-- @param openmw.core#GameObject object +-- @return #boolean + +--- +-- Whether the door is a teleport. +-- @function [parent=#ESM4Door] isTeleport +-- @param openmw.core#GameObject object +-- @return #boolean + +--- +-- Destination (only if a teleport door). +-- @function [parent=#ESM4Door] destPosition +-- @param openmw.core#GameObject object +-- @return openmw.util#Vector3 + +--- +-- Destination rotation (only if a teleport door). +-- @function [parent=#ESM4Door] destRotation +-- @param openmw.core#GameObject object +-- @return openmw.util#Vector3 + +--- +-- Destination cell (only if a teleport door). +-- @function [parent=#ESM4Door] destCell +-- @param openmw.core#GameObject object +-- @return openmw.core#Cell + +--- +-- Returns the read-only @{#ESM4DoorRecord} of a door +-- @function [parent=#ESM4Door] record +-- @param #any objectOrRecordId +-- @return #ESM4DoorRecord + +--- +-- Returns a read-only list of all @{#ESM4DoorRecord}s in the world database. +-- @function [parent=#ESM4Door] records +-- @return #list<#ESM4DoorRecord> + +--- +-- @type ESM4DoorRecord +-- @field #string id Record id +-- @field #string name Human-readable name +-- @field #string model VFS path to the model + return nil