diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 7eb790cf09..2c8eb8d155 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -90,7 +90,7 @@ add_openmw_dir (mwphysics add_openmw_dir (mwclass classes activator creature npc weapon armor potion apparatus book clothing container door ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor bodypart - light4 + esm4base light4 ) add_openmw_dir (mwmechanics diff --git a/apps/openmw/mwclass/classes.cpp b/apps/openmw/mwclass/classes.cpp index 1e62b0e18e..a60bdc57af 100644 --- a/apps/openmw/mwclass/classes.cpp +++ b/apps/openmw/mwclass/classes.cpp @@ -25,6 +25,8 @@ #include "static.hpp" #include "weapon.hpp" +#include "esm4base.hpp" + namespace MWClass { void registerClasses() @@ -51,18 +53,18 @@ namespace MWClass Static::registerSelf(); BodyPart::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); - ESM4Static::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Named::registerSelf(); + ESM4Static::registerSelf(); + ESM4Named::registerSelf(); ESM4Light::registerSelf(); } } diff --git a/apps/openmw/mwclass/esm4base.cpp b/apps/openmw/mwclass/esm4base.cpp new file mode 100644 index 0000000000..a99ecda928 --- /dev/null +++ b/apps/openmw/mwclass/esm4base.cpp @@ -0,0 +1,44 @@ +#include "esm4base.hpp" + +#include + +#include + +#include "../mwgui/tooltips.hpp" +#include "../mwgui/ustring.hpp" + +#include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" +#include "../mwrender/vismask.hpp" + +#include "../mwphysics/physicssystem.hpp" +#include "../mwworld/ptr.hpp" + +#include "classmodel.hpp" + +namespace MWClass +{ + void ESM4Impl::insertObjectRendering( + const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) + { + if (!model.empty()) + { + renderingInterface.getObjects().insertModel(ptr, model); + ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); + } + } + + void ESM4Impl::insertObjectPhysics( + const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) + { + physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World); + } + + MWGui::ToolTipInfo ESM4Impl::getToolTipInfo(std::string_view name, int count) + { + MWGui::ToolTipInfo info; + info.caption + = MyGUI::TextIterator::toTagsString(MWGui::toUString(name)) + MWGui::ToolTips::getCountString(count); + return info; + } +} diff --git a/apps/openmw/mwclass/esm4base.hpp b/apps/openmw/mwclass/esm4base.hpp new file mode 100644 index 0000000000..a06ffa881a --- /dev/null +++ b/apps/openmw/mwclass/esm4base.hpp @@ -0,0 +1,112 @@ +#ifndef GAME_MWCLASS_ESM4BASE_H +#define GAME_MWCLASS_ESM4BASE_H + +#include + +#include "../mwgui/tooltips.hpp" + +#include "../mwworld/cellstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/registeredclass.hpp" + +#include "classmodel.hpp" + +namespace MWClass +{ + + namespace ESM4Impl + { + void insertObjectRendering( + const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface); + void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, + MWPhysics::PhysicsSystem& physics); + MWGui::ToolTipInfo getToolTipInfo(std::string_view name, int count); + } + + // Base for all ESM4 Classes + template + class ESM4Base : public MWWorld::Class + { + + MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const override + { + const MWWorld::LiveCellRef* ref = ptr.get(); + return MWWorld::Ptr(cell.insert(ref), &cell); + } + + protected: + explicit ESM4Base(unsigned type) + : MWWorld::Class(type) + { + } + + public: + void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model, + MWRender::RenderingInterface& renderingInterface) const override + { + const MWWorld::LiveCellRef* ref = ptr.get(); + if (ref->mBase->mFlags & ESM4::Rec_Marker) + return; + ESM4Impl::insertObjectRendering(ptr, model, renderingInterface); + } + + void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, + MWPhysics::PhysicsSystem& physics) const override + { + insertObjectPhysics(ptr, model, rotation, physics); + } + + void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, + MWPhysics::PhysicsSystem& physics) const override + { + const MWWorld::LiveCellRef* ref = ptr.get(); + if (ref->mBase->mFlags & ESM4::Rec_Marker) + return; + ESM4Impl::insertObjectPhysics(ptr, model, rotation, physics); + } + + bool hasToolTip(const MWWorld::ConstPtr& ptr) const override { return false; } + + std::string_view getName(const MWWorld::ConstPtr& ptr) const override { return ""; } + + std::string getModel(const MWWorld::ConstPtr& ptr) const override { return getClassModel(ptr); } + }; + + class ESM4Static final : public MWWorld::RegisteredClass> + { + friend MWWorld::RegisteredClass>; + ESM4Static() + : MWWorld::RegisteredClass>(ESM4::Static::sRecordId) + { + } + }; + + // For records with `mFullName` that should be shown as a tooltip. + // All objects with a tooltip can be activated (activation can be handled in Lua). + template + class ESM4Named : public MWWorld::RegisteredClass, ESM4Base> + { + public: + friend MWWorld::RegisteredClass>; + + ESM4Named() + : MWWorld::RegisteredClass>(Record::sRecordId) + { + } + + public: + bool hasToolTip(const MWWorld::ConstPtr& ptr) const override { return true; } + + MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override + { + return ESM4Impl::getToolTipInfo(ptr.get()->mBase->mFullName, count); + } + + std::string_view getName(const MWWorld::ConstPtr& ptr) const override + { + return ptr.get()->mBase->mFullName; + } + }; +} + +#endif // GAME_MWCLASS_ESM4BASE_H diff --git a/apps/openmw/mwclass/light4.cpp b/apps/openmw/mwclass/light4.cpp index 8e3341fbb1..8c2cd1fdaa 100644 --- a/apps/openmw/mwclass/light4.cpp +++ b/apps/openmw/mwclass/light4.cpp @@ -12,7 +12,7 @@ namespace MWClass { ESM4Light::ESM4Light() - : MWWorld::RegisteredClass(ESM4::Light::sRecordId) + : MWWorld::RegisteredClass>(ESM4::Light::sRecordId) { } @@ -24,38 +24,4 @@ namespace MWClass // Insert even if model is empty, so that the light is added renderingInterface.getObjects().insertModel(ptr, model, !(ref->mBase->mData.flags & ESM4::Light::OffDefault)); } - - void ESM4Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, - MWPhysics::PhysicsSystem& physics) const - { - insertObjectPhysics(ptr, model, rotation, physics); - } - - void ESM4Light::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, - MWPhysics::PhysicsSystem& physics) const - { - physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World); - } - - std::string ESM4Light::getModel(const MWWorld::ConstPtr& ptr) const - { - return getClassModel(ptr); - } - - std::string_view ESM4Light ::getName(const MWWorld::ConstPtr& ptr) const - { - return {}; - } - - bool ESM4Light::hasToolTip(const MWWorld::ConstPtr& ptr) const - { - return false; - } - - MWWorld::Ptr ESM4Light::copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const - { - const MWWorld::LiveCellRef* ref = ptr.get(); - - return MWWorld::Ptr(cell.insert(ref), &cell); - } } diff --git a/apps/openmw/mwclass/light4.hpp b/apps/openmw/mwclass/light4.hpp index 6145281298..755f6b580d 100644 --- a/apps/openmw/mwclass/light4.hpp +++ b/apps/openmw/mwclass/light4.hpp @@ -1,34 +1,22 @@ #ifndef OPENW_MWCLASS_LIGHT4 #define OPENW_MWCLASS_LIGHT4 + #include "../mwworld/registeredclass.hpp" +#include "esm4base.hpp" + namespace MWClass { - class ESM4Light : public MWWorld::RegisteredClass + class ESM4Light : public MWWorld::RegisteredClass> { - friend MWWorld::RegisteredClass; + friend MWWorld::RegisteredClass>; ESM4Light(); - MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const override; - public: void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override; ///< Add reference into a cell for rendering - - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, - MWPhysics::PhysicsSystem& physics) const override; - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, - MWPhysics::PhysicsSystem& physics) const override; - - std::string_view getName(const MWWorld::ConstPtr& ptr) const override; - ///< \return name or ID; can return an empty string. - - bool hasToolTip(const MWWorld::ConstPtr& ptr) const override; - ///< @return true if this object has a tooltip when focused (default implementation: true) - - std::string getModel(const MWWorld::ConstPtr& ptr) const override; }; } #endif diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 82e974c7e0..fe1226f19b 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -64,20 +64,4 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } - - void ESM4StaticImpl::insertObjectRendering( - const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) - { - if (!model.empty()) - { - renderingInterface.getObjects().insertModel(ptr, model); - ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); - } - } - - void ESM4StaticImpl::insertObjectPhysics( - const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) - { - physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World); - } } diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index f9f7241055..0d853211bb 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -1,11 +1,15 @@ #ifndef GAME_MWCLASS_STATIC_H #define GAME_MWCLASS_STATIC_H +#include "../mwgui/tooltips.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/registeredclass.hpp" #include "classmodel.hpp" +#include "../mwgui/ustring.hpp" +#include + namespace MWClass { class Static : public MWWorld::RegisteredClass @@ -34,64 +38,6 @@ namespace MWClass std::string getModel(const MWWorld::ConstPtr& ptr) const override; }; - - namespace ESM4StaticImpl - { - void insertObjectRendering( - const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface); - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, - MWPhysics::PhysicsSystem& physics); - } - - // Templated because it is used as a dummy MWClass implementation for several ESM4 recors - template - class ESM4Static : public MWWorld::RegisteredClass> - { - friend MWWorld::RegisteredClass; - - ESM4Static() - : MWWorld::RegisteredClass(Record::sRecordId) - { - } - - MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const override - { - const MWWorld::LiveCellRef* ref = ptr.get(); - return MWWorld::Ptr(cell.insert(ref), &cell); - } - - public: - void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model, - MWRender::RenderingInterface& renderingInterface) const override - { - const MWWorld::LiveCellRef* ref = ptr.get(); - if (ref->mBase->mFlags & ESM4::Rec_Marker) - return; - ESM4StaticImpl::insertObjectRendering(ptr, model, renderingInterface); - } - - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, - MWPhysics::PhysicsSystem& physics) const override - { - insertObjectPhysics(ptr, model, rotation, physics); - } - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, - MWPhysics::PhysicsSystem& physics) const override - { - const MWWorld::LiveCellRef* ref = ptr.get(); - if (ref->mBase->mFlags & ESM4::Rec_Marker) - return; - ESM4StaticImpl::insertObjectPhysics(ptr, model, rotation, physics); - } - - std::string_view getName(const MWWorld::ConstPtr& ptr) const override { return ""; } - ///< \return name or ID; can return an empty string. - - bool hasToolTip(const MWWorld::ConstPtr& ptr) const override { return false; } - ///< @return true if this object has a tooltip when focused (default implementation: true) - - std::string getModel(const MWWorld::ConstPtr& ptr) const override { return getClassModel(ptr); } - }; } #endif diff --git a/apps/openmw/mwlua/cellbindings.cpp b/apps/openmw/mwlua/cellbindings.cpp index f144336fa0..d8d4e66d91 100644 --- a/apps/openmw/mwlua/cellbindings.cpp +++ b/apps/openmw/mwlua/cellbindings.cpp @@ -158,6 +158,50 @@ namespace MWLua case ESM::REC_REPA: cell.mStore->template forEachType(visitor); break; + case ESM::REC_STAT: + cell.mStore->template forEachType(visitor); + break; + + case ESM::REC_ACTI4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_AMMO4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_ARMO4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_BOOK4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_CLOT4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_CONT4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_DOOR4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_INGR4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_LIGH4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_MISC4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_ALCH4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_STAT4: + cell.mStore->template forEachType(visitor); + break; + case ESM::REC_WEAP4: + cell.mStore->template forEachType(visitor); + break; + default: ok = false; } 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..2e7ab7b748 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 { @@ -25,7 +30,7 @@ namespace MWWorld const ESM::RefNum& CellRef::getRefNum() const { return std::visit(ESM::VisitOverload{ - [&](const ESM4::Reference& ref) -> const ESM::RefNum& { return ref.mFormId; }, + [&](const ESM4::Reference& ref) -> const ESM::RefNum& { return ref.mId; }, [&](const ESM::CellRef& ref) -> const ESM::RefNum& { return ref.mRefNum; }, }, mCellRef.mVariant); @@ -34,7 +39,7 @@ namespace MWWorld const ESM::RefNum& CellRef::getOrAssignRefNum(ESM::RefNum& lastAssignedRefNum) { ESM::RefNum& refNum = std::visit(ESM::VisitOverload{ - [&](ESM4::Reference& ref) -> ESM::RefNum& { return ref.mFormId; }, + [&](ESM4::Reference& ref) -> ESM::RefNum& { return ref.mId; }, [&](ESM::CellRef& ref) -> ESM::RefNum& { return ref.mRefNum; }, }, mCellRef.mVariant); @@ -59,7 +64,7 @@ namespace MWWorld void CellRef::unsetRefNum() { std::visit(ESM::VisitOverload{ - [&](ESM4::Reference& ref) { ref.mFormId = emptyRefNum; }, + [&](ESM4::Reference& ref) { ref.mId = emptyRefNum; }, [&](ESM::CellRef& ref) { ref.mRefNum = emptyRefNum; }, }, mCellRef.mVariant); @@ -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( + 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/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 0b8b8150a1..ad1a3ff428 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -188,7 +188,7 @@ namespace MWWorld auto recordType = static_cast(reader.hdr().record.typeId); ESM::RecNameInts esm4RecName = static_cast(ESM::esm4Recname(recordType)); - if constexpr (std::is_convertible_v*, DynamicStore*> && HasRecordId::value) + if constexpr (HasRecordId::value) { if constexpr (ESM::isESM4Rec(T::sRecordId)) { diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 280c682d70..b73677d39c 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -16,8 +16,8 @@ namespace { // TODO: Switch to C++23 to get a working version of std::unordered_map::erase - template - bool eraseFromMap(T& map, const ESM::RefId& value) + template + bool eraseFromMap(T& map, const Id& value) { auto it = map.find(value); if (it != map.end()) @@ -95,19 +95,19 @@ namespace MWWorld template class IndexedStore; template class IndexedStore; - template - TypedDynamicStore::TypedDynamicStore() + template + TypedDynamicStore::TypedDynamicStore() { } - template - TypedDynamicStore::TypedDynamicStore(const TypedDynamicStore& orig) + template + TypedDynamicStore::TypedDynamicStore(const TypedDynamicStore& orig) : mStatic(orig.mStatic) { } - template - void TypedDynamicStore::clearDynamic() + template + void TypedDynamicStore::clearDynamic() { // remove the dynamic part of mShared assert(mShared.size() >= mStatic.size()); @@ -115,8 +115,8 @@ namespace MWWorld mDynamic.clear(); } - template - const T* TypedDynamicStore::search(const ESM::RefId& id) const + template + const T* TypedDynamicStore::search(const Id& id) const { typename Dynamic::const_iterator dit = mDynamic.find(id); if (dit != mDynamic.end()) @@ -128,8 +128,8 @@ namespace MWWorld return nullptr; } - template - const T* TypedDynamicStore::searchStatic(const ESM::RefId& id) const + template + const T* TypedDynamicStore::searchStatic(const Id& id) const { typename Static::const_iterator it = mStatic.find(id); if (it != mStatic.end()) @@ -138,24 +138,29 @@ namespace MWWorld return nullptr; } - template - bool TypedDynamicStore::isDynamic(const ESM::RefId& id) const + template + bool TypedDynamicStore::isDynamic(const Id& id) const { typename Dynamic::const_iterator dit = mDynamic.find(id); return (dit != mDynamic.end()); } - template - const T* TypedDynamicStore::searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const + template + const T* TypedDynamicStore::searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const { - std::vector results; - std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results), - [prefix](const T* item) { return item->mId.startsWith(prefix); }); - if (!results.empty()) - return results[Misc::Rng::rollDice(results.size(), prng)]; - return nullptr; + if constexpr (std::is_same_v) + { + std::vector results; + std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results), + [prefix](const T* item) { return item->mId.startsWith(prefix); }); + if (!results.empty()) + return results[Misc::Rng::rollDice(results.size(), prng)]; + return nullptr; + } + else + throw std::runtime_error("Store::searchRandom is supported only if Id is ESM::RefId"); } - template - const T* TypedDynamicStore::find(const ESM::RefId& id) const + template + const T* TypedDynamicStore::find(const Id& id) const { const T* ptr = search(id); if (ptr == nullptr) @@ -174,8 +179,8 @@ namespace MWWorld } return ptr; } - template - RecordId TypedDynamicStore::load(ESM::ESMReader& esm) + template + RecordId TypedDynamicStore::load(ESM::ESMReader& esm) { T record; bool isDeleted = false; @@ -188,37 +193,41 @@ namespace MWWorld if (inserted.second) mShared.push_back(&inserted.first->second); - return RecordId(record.mId, isDeleted); + if constexpr (std::is_same_v) + return RecordId(record.mId, isDeleted); + else + return RecordId(); } - template - void TypedDynamicStore::setUp() + + template + void TypedDynamicStore::setUp() { } - template - typename TypedDynamicStore::iterator TypedDynamicStore::begin() const + template + typename TypedDynamicStore::iterator TypedDynamicStore::begin() const { return mShared.begin(); } - template - typename TypedDynamicStore::iterator TypedDynamicStore::end() const + template + typename TypedDynamicStore::iterator TypedDynamicStore::end() const { return mShared.end(); } - template - size_t TypedDynamicStore::getSize() const + template + size_t TypedDynamicStore::getSize() const { return mShared.size(); } - template - int TypedDynamicStore::getDynamicSize() const + template + int TypedDynamicStore::getDynamicSize() const { return mDynamic.size(); } - template - void TypedDynamicStore::listIdentifier(std::vector& list) const + template + void TypedDynamicStore::listIdentifier(std::vector& list) const { list.reserve(list.size() + getSize()); typename std::vector::const_iterator it = mShared.begin(); @@ -227,8 +236,8 @@ namespace MWWorld list.push_back((*it)->mId); } } - template - T* TypedDynamicStore::insert(const T& item, bool overrideOnly) + template + T* TypedDynamicStore::insert(const T& item, bool overrideOnly) { if (overrideOnly) { @@ -242,8 +251,8 @@ namespace MWWorld mShared.push_back(ptr); return ptr; } - template - T* TypedDynamicStore::insertStatic(const T& item) + template + T* TypedDynamicStore::insertStatic(const T& item) { std::pair result = mStatic.insert_or_assign(item.mId, item); T* ptr = &result.first->second; @@ -251,8 +260,8 @@ namespace MWWorld mShared.push_back(ptr); return ptr; } - template - bool TypedDynamicStore::eraseStatic(const ESM::RefId& id) + template + bool TypedDynamicStore::eraseStatic(const Id& id) { typename Static::iterator it = mStatic.find(id); @@ -277,8 +286,8 @@ namespace MWWorld return true; } - template - bool TypedDynamicStore::erase(const ESM::RefId& id) + template + bool TypedDynamicStore::erase(const Id& id) { if (!eraseFromMap(mDynamic, id)) return false; @@ -292,13 +301,13 @@ namespace MWWorld } return true; } - template - bool TypedDynamicStore::erase(const T& item) + template + bool TypedDynamicStore::erase(const T& item) { return erase(item.mId); } - template - void TypedDynamicStore::write(ESM::ESMWriter& writer, Loading::Listener& progress) const + template + void TypedDynamicStore::write(ESM::ESMWriter& writer, Loading::Listener& progress) const { for (typename Dynamic::const_iterator iter(mDynamic.begin()); iter != mDynamic.end(); ++iter) { @@ -310,8 +319,8 @@ namespace MWWorld } } } - template - RecordId TypedDynamicStore::read(ESM::ESMReader& reader, bool overrideOnly) + template + RecordId TypedDynamicStore::read(ESM::ESMReader& reader, bool overrideOnly) { T record; bool isDeleted = false; @@ -321,7 +330,10 @@ namespace MWWorld } insert(record, overrideOnly); - return RecordId(record.mId, isDeleted); + if constexpr (std::is_same_v) + return RecordId(record.mId, isDeleted); + else + return RecordId(); } // LandTexture @@ -1232,6 +1244,6 @@ template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; -template class MWWorld::TypedDynamicStore; +template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 58636acdb6..8f7a69ebe3 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -52,22 +53,23 @@ namespace MWWorld { }; // Empty interface to be parent of all store types - class DynamicStore : public StoreBase + template + class DynamicStoreBase : public StoreBase { public: - virtual ~DynamicStore() {} + virtual ~DynamicStoreBase() {} virtual void setUp() {} /// List identifiers of records contained in this Store (case-smashed). No-op for Stores that don't use string /// IDs. - virtual void listIdentifier(std::vector& list) const {} + virtual void listIdentifier(std::vector& list) const {} virtual size_t getSize() const = 0; virtual int getDynamicSize() const { return 0; } virtual RecordId load(ESM::ESMReader& esm) = 0; - virtual bool eraseStatic(const ESM::RefId& id) { return false; } + virtual bool eraseStatic(const Id& id) { return false; } virtual void clearDynamic() {} virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) const {} @@ -76,6 +78,8 @@ namespace MWWorld ///< Read into dynamic storage }; + using DynamicStore = DynamicStoreBase; + template class IndexedStore : public StoreBase { @@ -169,23 +173,23 @@ namespace MWWorld class ESMStore; - template - class TypedDynamicStore : public DynamicStore + template + class TypedDynamicStore : public DynamicStoreBase { - typedef std::unordered_map Static; + typedef std::unordered_map Static; Static mStatic; /// @par mShared usually preserves the record order as it came from the content files (this /// is relevant for the spell autocalc code and selection order /// for heads/hairs in the character creation) std::vector mShared; - typedef std::unordered_map Dynamic; + typedef std::unordered_map Dynamic; Dynamic mDynamic; friend class ESMStore; public: TypedDynamicStore(); - TypedDynamicStore(const TypedDynamicStore& orig); + TypedDynamicStore(const TypedDynamicStore& orig); typedef SharedIterator iterator; @@ -193,19 +197,19 @@ namespace MWWorld void clearDynamic() override; void setUp() override; - const T* search(const ESM::RefId& id) const; - const T* searchStatic(const ESM::RefId& id) const; + const T* search(const Id& id) const; + const T* searchStatic(const Id& id) const; /** * Does the record with this ID come from the dynamic store? */ - bool isDynamic(const ESM::RefId& id) const; + bool isDynamic(const Id& id) const; /** Returns a random record that starts with the named ID, or nullptr if not found. */ const T* searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const; // calls `search` and throws an exception if not found - const T* find(const ESM::RefId& id) const; + const T* find(const Id& id) const; iterator begin() const; iterator end() const; @@ -215,13 +219,13 @@ namespace MWWorld int getDynamicSize() const override; /// @note The record identifiers are listed in the order that the records were defined by the content files. - void listIdentifier(std::vector& list) const override; + void listIdentifier(std::vector& list) const override; T* insert(const T& item, bool overrideOnly = false); T* insertStatic(const T& item); - bool eraseStatic(const ESM::RefId& id) override; - bool erase(const ESM::RefId& id); + bool eraseStatic(const Id& id) override; + bool erase(const Id& id); bool erase(const T& item); RecordId load(ESM::ESMReader& esm) override; @@ -533,6 +537,11 @@ namespace MWWorld const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() const; }; + template <> + class Store : public TypedDynamicStore + { + }; + } // end namespace #endif 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/components/esm4/loadrefr.cpp b/components/esm4/loadrefr.cpp index 23b4f68c54..97151c8898 100644 --- a/components/esm4/loadrefr.cpp +++ b/components/esm4/loadrefr.cpp @@ -34,9 +34,8 @@ void ESM4::Reference::load(ESM4::Reader& reader) { - mFormId = reader.hdr().record.getFormId(); - reader.adjustFormId(mFormId); - mId = ESM::RefId::formIdRefId(mFormId); + mId = reader.hdr().record.getFormId(); + reader.adjustFormId(mId); mFlags = reader.hdr().record.flags; mParentFormId = reader.currCell(); // NOTE: only for persistent refs? mParent = ESM::RefId::formIdRefId(mParentFormId); diff --git a/components/esm4/loadrefr.hpp b/components/esm4/loadrefr.hpp index fdeff9fd82..4ae4aea757 100644 --- a/components/esm4/loadrefr.hpp +++ b/components/esm4/loadrefr.hpp @@ -74,8 +74,7 @@ namespace ESM4 struct Reference { - FormId mFormId; // from the header - ESM::RefId mId; + FormId mId; // from the header FormId mParentFormId; // cell FormId (currently persistent refs only), from the loading sequence // NOTE: for exterior cells it will be the dummy cell FormId 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