1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-20 14:41:34 +00:00

Merge branch 'lua_esm4' into 'master'

Initial Lua bindings for ESM4 types

See merge request OpenMW/openmw!2916
This commit is contained in:
psi29a 2023-04-20 07:28:11 +00:00
commit 5212f4f623
22 changed files with 520 additions and 233 deletions

View file

@ -90,7 +90,7 @@ add_openmw_dir (mwphysics
add_openmw_dir (mwclass add_openmw_dir (mwclass
classes activator creature npc weapon armor potion apparatus book clothing container door classes activator creature npc weapon armor potion apparatus book clothing container door
ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor bodypart ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor bodypart
light4 esm4base light4
) )
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics

View file

@ -25,6 +25,8 @@
#include "static.hpp" #include "static.hpp"
#include "weapon.hpp" #include "weapon.hpp"
#include "esm4base.hpp"
namespace MWClass namespace MWClass
{ {
void registerClasses() void registerClasses()
@ -51,18 +53,18 @@ namespace MWClass
Static::registerSelf(); Static::registerSelf();
BodyPart::registerSelf(); BodyPart::registerSelf();
ESM4Static<ESM4::Activator>::registerSelf(); ESM4Named<ESM4::Activator>::registerSelf();
ESM4Static<ESM4::Potion>::registerSelf(); ESM4Named<ESM4::Potion>::registerSelf();
ESM4Static<ESM4::Ammunition>::registerSelf(); ESM4Named<ESM4::Ammunition>::registerSelf();
ESM4Static<ESM4::Armor>::registerSelf(); ESM4Named<ESM4::Armor>::registerSelf();
ESM4Static<ESM4::Book>::registerSelf(); ESM4Named<ESM4::Book>::registerSelf();
ESM4Static<ESM4::Clothing>::registerSelf(); ESM4Named<ESM4::Clothing>::registerSelf();
ESM4Static<ESM4::Container>::registerSelf(); ESM4Named<ESM4::Container>::registerSelf();
ESM4Static<ESM4::Door>::registerSelf(); ESM4Named<ESM4::Door>::registerSelf();
ESM4Static<ESM4::Ingredient>::registerSelf(); ESM4Named<ESM4::Ingredient>::registerSelf();
ESM4Static<ESM4::MiscItem>::registerSelf(); ESM4Named<ESM4::MiscItem>::registerSelf();
ESM4Static<ESM4::Static>::registerSelf(); ESM4Static::registerSelf();
ESM4Static<ESM4::Weapon>::registerSelf(); ESM4Named<ESM4::Weapon>::registerSelf();
ESM4Light::registerSelf(); ESM4Light::registerSelf();
} }
} }

View file

@ -0,0 +1,44 @@
#include "esm4base.hpp"
#include <MyGUI_TextIterator.h>
#include <components/sceneutil/positionattitudetransform.hpp>
#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;
}
}

View file

@ -0,0 +1,112 @@
#ifndef GAME_MWCLASS_ESM4BASE_H
#define GAME_MWCLASS_ESM4BASE_H
#include <components/esm4/loadstat.hpp>
#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 <typename Record>
class ESM4Base : public MWWorld::Class
{
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const override
{
const MWWorld::LiveCellRef<Record>* ref = ptr.get<Record>();
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<Record>* ref = ptr.get<Record>();
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<Record>* ref = ptr.get<Record>();
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<Record>(ptr); }
};
class ESM4Static final : public MWWorld::RegisteredClass<ESM4Static, ESM4Base<ESM4::Static>>
{
friend MWWorld::RegisteredClass<ESM4Static, ESM4Base<ESM4::Static>>;
ESM4Static()
: MWWorld::RegisteredClass<ESM4Static, ESM4Base<ESM4::Static>>(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 <typename Record>
class ESM4Named : public MWWorld::RegisteredClass<ESM4Named<Record>, ESM4Base<Record>>
{
public:
friend MWWorld::RegisteredClass<ESM4Named, ESM4Base<Record>>;
ESM4Named()
: MWWorld::RegisteredClass<ESM4Named, ESM4Base<Record>>(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<Record>()->mBase->mFullName, count);
}
std::string_view getName(const MWWorld::ConstPtr& ptr) const override
{
return ptr.get<Record>()->mBase->mFullName;
}
};
}
#endif // GAME_MWCLASS_ESM4BASE_H

View file

@ -12,7 +12,7 @@
namespace MWClass namespace MWClass
{ {
ESM4Light::ESM4Light() ESM4Light::ESM4Light()
: MWWorld::RegisteredClass<ESM4Light>(ESM4::Light::sRecordId) : MWWorld::RegisteredClass<ESM4Light, ESM4Base<ESM4::Light>>(ESM4::Light::sRecordId)
{ {
} }
@ -24,38 +24,4 @@ namespace MWClass
// Insert even if model is empty, so that the light is added // Insert even if model is empty, so that the light is added
renderingInterface.getObjects().insertModel(ptr, model, !(ref->mBase->mData.flags & ESM4::Light::OffDefault)); 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<ESM4::Light>(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<ESM4::Light>* ref = ptr.get<ESM4::Light>();
return MWWorld::Ptr(cell.insert(ref), &cell);
}
} }

View file

@ -1,34 +1,22 @@
#ifndef OPENW_MWCLASS_LIGHT4 #ifndef OPENW_MWCLASS_LIGHT4
#define OPENW_MWCLASS_LIGHT4 #define OPENW_MWCLASS_LIGHT4
#include "../mwworld/registeredclass.hpp" #include "../mwworld/registeredclass.hpp"
#include "esm4base.hpp"
namespace MWClass namespace MWClass
{ {
class ESM4Light : public MWWorld::RegisteredClass<ESM4Light> class ESM4Light : public MWWorld::RegisteredClass<ESM4Light, ESM4Base<ESM4::Light>>
{ {
friend MWWorld::RegisteredClass<ESM4Light>; friend MWWorld::RegisteredClass<ESM4Light, ESM4Base<ESM4::Light>>;
ESM4Light(); ESM4Light();
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const override;
public: public:
void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model, void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model,
MWRender::RenderingInterface& renderingInterface) const override; MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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 #endif

View file

@ -64,20 +64,4 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell); 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);
}
} }

View file

@ -1,11 +1,15 @@
#ifndef GAME_MWCLASS_STATIC_H #ifndef GAME_MWCLASS_STATIC_H
#define GAME_MWCLASS_STATIC_H #define GAME_MWCLASS_STATIC_H
#include "../mwgui/tooltips.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/registeredclass.hpp" #include "../mwworld/registeredclass.hpp"
#include "classmodel.hpp" #include "classmodel.hpp"
#include "../mwgui/ustring.hpp"
#include <MyGUI_TextIterator.h>
namespace MWClass namespace MWClass
{ {
class Static : public MWWorld::RegisteredClass<Static> class Static : public MWWorld::RegisteredClass<Static>
@ -34,64 +38,6 @@ namespace MWClass
std::string getModel(const MWWorld::ConstPtr& ptr) const override; 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 <typename Record>
class ESM4Static : public MWWorld::RegisteredClass<ESM4Static<Record>>
{
friend MWWorld::RegisteredClass<ESM4Static>;
ESM4Static()
: MWWorld::RegisteredClass<ESM4Static>(Record::sRecordId)
{
}
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const override
{
const MWWorld::LiveCellRef<Record>* ref = ptr.get<Record>();
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<Record>* ref = ptr.get<Record>();
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<Record>* ref = ptr.get<Record>();
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<Record>(ptr); }
};
} }
#endif #endif

View file

@ -158,6 +158,50 @@ namespace MWLua
case ESM::REC_REPA: case ESM::REC_REPA:
cell.mStore->template forEachType<ESM::Repair>(visitor); cell.mStore->template forEachType<ESM::Repair>(visitor);
break; break;
case ESM::REC_STAT:
cell.mStore->template forEachType<ESM::Static>(visitor);
break;
case ESM::REC_ACTI4:
cell.mStore->template forEachType<ESM4::Activator>(visitor);
break;
case ESM::REC_AMMO4:
cell.mStore->template forEachType<ESM4::Ammunition>(visitor);
break;
case ESM::REC_ARMO4:
cell.mStore->template forEachType<ESM4::Armor>(visitor);
break;
case ESM::REC_BOOK4:
cell.mStore->template forEachType<ESM4::Book>(visitor);
break;
case ESM::REC_CLOT4:
cell.mStore->template forEachType<ESM4::Clothing>(visitor);
break;
case ESM::REC_CONT4:
cell.mStore->template forEachType<ESM4::Container>(visitor);
break;
case ESM::REC_DOOR4:
cell.mStore->template forEachType<ESM4::Door>(visitor);
break;
case ESM::REC_INGR4:
cell.mStore->template forEachType<ESM4::Ingredient>(visitor);
break;
case ESM::REC_LIGH4:
cell.mStore->template forEachType<ESM4::Light>(visitor);
break;
case ESM::REC_MISC4:
cell.mStore->template forEachType<ESM4::MiscItem>(visitor);
break;
case ESM::REC_ALCH4:
cell.mStore->template forEachType<ESM4::Potion>(visitor);
break;
case ESM::REC_STAT4:
cell.mStore->template forEachType<ESM4::Static>(visitor);
break;
case ESM::REC_WEAP4:
cell.mStore->template forEachType<ESM4::Weapon>(visitor);
break;
default: default:
ok = false; ok = false;
} }

View file

@ -1,6 +1,7 @@
#include "types.hpp" #include "types.hpp"
#include <components/esm3/loaddoor.hpp> #include <components/esm3/loaddoor.hpp>
#include <components/esm4/loaddoor.hpp>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
@ -15,6 +16,11 @@ namespace sol
struct is_automagical<ESM::Door> : std::false_type struct is_automagical<ESM::Door> : std::false_type
{ {
}; };
template <>
struct is_automagical<ESM4::Door> : std::false_type
{
};
} }
namespace MWLua namespace MWLua
@ -25,6 +31,11 @@ namespace MWLua
return verifyType(ESM::REC_DOOR, o.ptr()); 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) void addDoorBindings(sol::table door, const Context& context)
{ {
door["isTeleport"] = [](const Object& o) { return doorPtr(o).getCellRef().getTeleport(); }; 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(); }); [](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<ESM4::Door>(door, context, "ESM4Door");
sol::usertype<ESM4::Door> record = context.mLua->sol().new_usertype<ESM4::Door>("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);
});
}
} }

View file

@ -32,6 +32,20 @@ namespace MWLua
constexpr std::string_view Probe = "Probe"; constexpr std::string_view Probe = "Probe";
constexpr std::string_view Repair = "Repair"; constexpr std::string_view Repair = "Repair";
constexpr std::string_view Marker = "Marker"; 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 namespace
@ -57,6 +71,20 @@ namespace MWLua
{ ESM::REC_LOCK, ObjectTypeName::Lockpick }, { ESM::REC_LOCK, ObjectTypeName::Lockpick },
{ ESM::REC_PROB, ObjectTypeName::Probe }, { ESM::REC_PROB, ObjectTypeName::Probe },
{ ESM::REC_REPA, ObjectTypeName::Repair }, { 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); addDoorBindings(addType(ObjectTypeName::Door, { ESM::REC_DOOR }), context);
addStaticBindings(addType(ObjectTypeName::Static, { ESM::REC_STAT }), 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 typeToPackage = getTypeToPackageTable(context.mLua->sol());
sol::table packageToType = getPackageToTypeTable(context.mLua->sol()); sol::table packageToType = getPackageToTypeTable(context.mLua->sol());
for (const auto& [type, name] : luaObjectTypeInfo) for (const auto& [type, name] : luaObjectTypeInfo)

View file

@ -63,8 +63,11 @@ namespace MWLua
void addStaticBindings(sol::table stat, const Context& context); void addStaticBindings(sol::table stat, const Context& context);
void addLightBindings(sol::table light, const Context& context); void addLightBindings(sol::table light, const Context& context);
void addESM4DoorBindings(sol::table door, const Context& context);
template <class T> template <class T>
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<T>& store = MWBase::Environment::get().getWorld()->getStore().get<T>(); const MWWorld::Store<T>& store = MWBase::Environment::get().getWorld()->getStore().get<T>();
@ -75,9 +78,9 @@ namespace MWLua
// Provide the interface of a read-only array. // Provide the interface of a read-only array.
using StoreT = MWWorld::Store<T>; using StoreT = MWWorld::Store<T>;
sol::state_view& lua = context.mLua->sol(); sol::state_view& lua = context.mLua->sol();
sol::usertype<StoreT> storeT = lua.new_usertype<StoreT>(std::string(T::getRecordType()) + "WorldStore"); sol::usertype<StoreT> storeT = lua.new_usertype<StoreT>(recordName + "WorldStore");
storeT[sol::meta_function::to_string] = [](const StoreT& store) { storeT[sol::meta_function::to_string] = [recordName](const StoreT& store) {
return "{" + std::to_string(store.getSize()) + " " + std::string(T::getRecordType()) + " records}"; return "{" + std::to_string(store.getSize()) + " " + recordName + " records}";
}; };
storeT[sol::meta_function::length] = [](const StoreT& store) { return store.getSize(); }; storeT[sol::meta_function::length] = [](const StoreT& store) { return store.getSize(); };
storeT[sol::meta_function::index] = [](const StoreT& store, size_t index) -> const T* { storeT[sol::meta_function::index] = [](const StoreT& store, size_t index) -> const T* {

View file

@ -3,10 +3,15 @@
#include <cassert> #include <cassert>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/esm/refid.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/esm3/objectstate.hpp> #include <components/esm3/objectstate.hpp>
#include <components/esm4/loadrefr.hpp>
#include <apps/openmw/mwbase/environment.hpp>
#include <apps/openmw/mwbase/world.hpp>
#include <apps/openmw/mwworld/cellutils.hpp> #include <apps/openmw/mwworld/cellutils.hpp>
#include <apps/openmw/mwworld/esmstore.hpp>
namespace MWWorld namespace MWWorld
{ {
@ -25,7 +30,7 @@ namespace MWWorld
const ESM::RefNum& CellRef::getRefNum() const const ESM::RefNum& CellRef::getRefNum() const
{ {
return std::visit(ESM::VisitOverload{ 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; }, [&](const ESM::CellRef& ref) -> const ESM::RefNum& { return ref.mRefNum; },
}, },
mCellRef.mVariant); mCellRef.mVariant);
@ -34,7 +39,7 @@ namespace MWWorld
const ESM::RefNum& CellRef::getOrAssignRefNum(ESM::RefNum& lastAssignedRefNum) const ESM::RefNum& CellRef::getOrAssignRefNum(ESM::RefNum& lastAssignedRefNum)
{ {
ESM::RefNum& refNum = std::visit(ESM::VisitOverload{ 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; }, [&](ESM::CellRef& ref) -> ESM::RefNum& { return ref.mRefNum; },
}, },
mCellRef.mVariant); mCellRef.mVariant);
@ -59,7 +64,7 @@ namespace MWWorld
void CellRef::unsetRefNum() void CellRef::unsetRefNum()
{ {
std::visit(ESM::VisitOverload{ std::visit(ESM::VisitOverload{
[&](ESM4::Reference& ref) { ref.mFormId = emptyRefNum; }, [&](ESM4::Reference& ref) { ref.mId = emptyRefNum; },
[&](ESM::CellRef& ref) { ref.mRefNum = emptyRefNum; }, [&](ESM::CellRef& ref) { ref.mRefNum = emptyRefNum; },
}, },
mCellRef.mVariant); mCellRef.mVariant);
@ -90,13 +95,18 @@ namespace MWWorld
return ESM::RefId::esm3ExteriorCell(index.x(), index.y()); 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<ESM4::Reference>().searchStatic(
ref.mDoor.destDoor);
if (refDest)
return refDest->mParent;
return ESM::RefId::sEmpty;
};
return std::visit( return std::visit(ESM::VisitOverload{ esm3Visit, esm4Visit }, mCellRef.mVariant);
ESM::VisitOverload{
[&](const ESM4::Reference& ref) -> ESM::RefId { return ESM::RefId::sEmpty; },
esm3Visit,
},
mCellRef.mVariant);
} }
void CellRef::setScale(float scale) void CellRef::setScale(float scale)

View file

@ -55,7 +55,7 @@ namespace MWWorld
struct Visitor struct Visitor
{ {
bool operator()(const ESM::CellRef& ref) { return ref.mTeleport; } 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); return std::visit(Visitor(), mCellRef.mVariant);
} }

View file

@ -188,7 +188,7 @@ namespace MWWorld
auto recordType = static_cast<ESM4::RecordTypes>(reader.hdr().record.typeId); auto recordType = static_cast<ESM4::RecordTypes>(reader.hdr().record.typeId);
ESM::RecNameInts esm4RecName = static_cast<ESM::RecNameInts>(ESM::esm4Recname(recordType)); ESM::RecNameInts esm4RecName = static_cast<ESM::RecNameInts>(ESM::esm4Recname(recordType));
if constexpr (std::is_convertible_v<Store<T>*, DynamicStore*> && HasRecordId<T>::value) if constexpr (HasRecordId<T>::value)
{ {
if constexpr (ESM::isESM4Rec(T::sRecordId)) if constexpr (ESM::isESM4Rec(T::sRecordId))
{ {

View file

@ -16,8 +16,8 @@
namespace namespace
{ {
// TODO: Switch to C++23 to get a working version of std::unordered_map::erase // TODO: Switch to C++23 to get a working version of std::unordered_map::erase
template <class T> template <class T, class Id>
bool eraseFromMap(T& map, const ESM::RefId& value) bool eraseFromMap(T& map, const Id& value)
{ {
auto it = map.find(value); auto it = map.find(value);
if (it != map.end()) if (it != map.end())
@ -95,19 +95,19 @@ namespace MWWorld
template class IndexedStore<ESM::MagicEffect>; template class IndexedStore<ESM::MagicEffect>;
template class IndexedStore<ESM::Skill>; template class IndexedStore<ESM::Skill>;
template <typename T> template <class T, class Id>
TypedDynamicStore<T>::TypedDynamicStore() TypedDynamicStore<T, Id>::TypedDynamicStore()
{ {
} }
template <typename T> template <class T, class Id>
TypedDynamicStore<T>::TypedDynamicStore(const TypedDynamicStore<T>& orig) TypedDynamicStore<T, Id>::TypedDynamicStore(const TypedDynamicStore<T, Id>& orig)
: mStatic(orig.mStatic) : mStatic(orig.mStatic)
{ {
} }
template <typename T> template <class T, class Id>
void TypedDynamicStore<T>::clearDynamic() void TypedDynamicStore<T, Id>::clearDynamic()
{ {
// remove the dynamic part of mShared // remove the dynamic part of mShared
assert(mShared.size() >= mStatic.size()); assert(mShared.size() >= mStatic.size());
@ -115,8 +115,8 @@ namespace MWWorld
mDynamic.clear(); mDynamic.clear();
} }
template <typename T> template <class T, class Id>
const T* TypedDynamicStore<T>::search(const ESM::RefId& id) const const T* TypedDynamicStore<T, Id>::search(const Id& id) const
{ {
typename Dynamic::const_iterator dit = mDynamic.find(id); typename Dynamic::const_iterator dit = mDynamic.find(id);
if (dit != mDynamic.end()) if (dit != mDynamic.end())
@ -128,8 +128,8 @@ namespace MWWorld
return nullptr; return nullptr;
} }
template <typename T> template <class T, class Id>
const T* TypedDynamicStore<T>::searchStatic(const ESM::RefId& id) const const T* TypedDynamicStore<T, Id>::searchStatic(const Id& id) const
{ {
typename Static::const_iterator it = mStatic.find(id); typename Static::const_iterator it = mStatic.find(id);
if (it != mStatic.end()) if (it != mStatic.end())
@ -138,24 +138,29 @@ namespace MWWorld
return nullptr; return nullptr;
} }
template <typename T> template <class T, class Id>
bool TypedDynamicStore<T>::isDynamic(const ESM::RefId& id) const bool TypedDynamicStore<T, Id>::isDynamic(const Id& id) const
{ {
typename Dynamic::const_iterator dit = mDynamic.find(id); typename Dynamic::const_iterator dit = mDynamic.find(id);
return (dit != mDynamic.end()); return (dit != mDynamic.end());
} }
template <typename T> template <class T, class Id>
const T* TypedDynamicStore<T>::searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const const T* TypedDynamicStore<T, Id>::searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const
{ {
std::vector<const T*> results; if constexpr (std::is_same_v<Id, ESM::RefId>)
std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results), {
[prefix](const T* item) { return item->mId.startsWith(prefix); }); std::vector<const T*> results;
if (!results.empty()) std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results),
return results[Misc::Rng::rollDice(results.size(), prng)]; [prefix](const T* item) { return item->mId.startsWith(prefix); });
return nullptr; if (!results.empty())
return results[Misc::Rng::rollDice(results.size(), prng)];
return nullptr;
}
else
throw std::runtime_error("Store<T>::searchRandom is supported only if Id is ESM::RefId");
} }
template <typename T> template <class T, class Id>
const T* TypedDynamicStore<T>::find(const ESM::RefId& id) const const T* TypedDynamicStore<T, Id>::find(const Id& id) const
{ {
const T* ptr = search(id); const T* ptr = search(id);
if (ptr == nullptr) if (ptr == nullptr)
@ -174,8 +179,8 @@ namespace MWWorld
} }
return ptr; return ptr;
} }
template <typename T> template <class T, class Id>
RecordId TypedDynamicStore<T>::load(ESM::ESMReader& esm) RecordId TypedDynamicStore<T, Id>::load(ESM::ESMReader& esm)
{ {
T record; T record;
bool isDeleted = false; bool isDeleted = false;
@ -188,37 +193,41 @@ namespace MWWorld
if (inserted.second) if (inserted.second)
mShared.push_back(&inserted.first->second); mShared.push_back(&inserted.first->second);
return RecordId(record.mId, isDeleted); if constexpr (std::is_same_v<Id, ESM::RefId>)
return RecordId(record.mId, isDeleted);
else
return RecordId();
} }
template <typename T>
void TypedDynamicStore<T>::setUp() template <class T, class Id>
void TypedDynamicStore<T, Id>::setUp()
{ {
} }
template <typename T> template <class T, class Id>
typename TypedDynamicStore<T>::iterator TypedDynamicStore<T>::begin() const typename TypedDynamicStore<T, Id>::iterator TypedDynamicStore<T, Id>::begin() const
{ {
return mShared.begin(); return mShared.begin();
} }
template <typename T> template <class T, class Id>
typename TypedDynamicStore<T>::iterator TypedDynamicStore<T>::end() const typename TypedDynamicStore<T, Id>::iterator TypedDynamicStore<T, Id>::end() const
{ {
return mShared.end(); return mShared.end();
} }
template <typename T> template <class T, class Id>
size_t TypedDynamicStore<T>::getSize() const size_t TypedDynamicStore<T, Id>::getSize() const
{ {
return mShared.size(); return mShared.size();
} }
template <typename T> template <class T, class Id>
int TypedDynamicStore<T>::getDynamicSize() const int TypedDynamicStore<T, Id>::getDynamicSize() const
{ {
return mDynamic.size(); return mDynamic.size();
} }
template <typename T> template <class T, class Id>
void TypedDynamicStore<T>::listIdentifier(std::vector<ESM::RefId>& list) const void TypedDynamicStore<T, Id>::listIdentifier(std::vector<Id>& list) const
{ {
list.reserve(list.size() + getSize()); list.reserve(list.size() + getSize());
typename std::vector<T*>::const_iterator it = mShared.begin(); typename std::vector<T*>::const_iterator it = mShared.begin();
@ -227,8 +236,8 @@ namespace MWWorld
list.push_back((*it)->mId); list.push_back((*it)->mId);
} }
} }
template <typename T> template <class T, class Id>
T* TypedDynamicStore<T>::insert(const T& item, bool overrideOnly) T* TypedDynamicStore<T, Id>::insert(const T& item, bool overrideOnly)
{ {
if (overrideOnly) if (overrideOnly)
{ {
@ -242,8 +251,8 @@ namespace MWWorld
mShared.push_back(ptr); mShared.push_back(ptr);
return ptr; return ptr;
} }
template <typename T> template <class T, class Id>
T* TypedDynamicStore<T>::insertStatic(const T& item) T* TypedDynamicStore<T, Id>::insertStatic(const T& item)
{ {
std::pair<typename Static::iterator, bool> result = mStatic.insert_or_assign(item.mId, item); std::pair<typename Static::iterator, bool> result = mStatic.insert_or_assign(item.mId, item);
T* ptr = &result.first->second; T* ptr = &result.first->second;
@ -251,8 +260,8 @@ namespace MWWorld
mShared.push_back(ptr); mShared.push_back(ptr);
return ptr; return ptr;
} }
template <typename T> template <class T, class Id>
bool TypedDynamicStore<T>::eraseStatic(const ESM::RefId& id) bool TypedDynamicStore<T, Id>::eraseStatic(const Id& id)
{ {
typename Static::iterator it = mStatic.find(id); typename Static::iterator it = mStatic.find(id);
@ -277,8 +286,8 @@ namespace MWWorld
return true; return true;
} }
template <typename T> template <class T, class Id>
bool TypedDynamicStore<T>::erase(const ESM::RefId& id) bool TypedDynamicStore<T, Id>::erase(const Id& id)
{ {
if (!eraseFromMap(mDynamic, id)) if (!eraseFromMap(mDynamic, id))
return false; return false;
@ -292,13 +301,13 @@ namespace MWWorld
} }
return true; return true;
} }
template <typename T> template <class T, class Id>
bool TypedDynamicStore<T>::erase(const T& item) bool TypedDynamicStore<T, Id>::erase(const T& item)
{ {
return erase(item.mId); return erase(item.mId);
} }
template <typename T> template <class T, class Id>
void TypedDynamicStore<T>::write(ESM::ESMWriter& writer, Loading::Listener& progress) const void TypedDynamicStore<T, Id>::write(ESM::ESMWriter& writer, Loading::Listener& progress) const
{ {
for (typename Dynamic::const_iterator iter(mDynamic.begin()); iter != mDynamic.end(); ++iter) for (typename Dynamic::const_iterator iter(mDynamic.begin()); iter != mDynamic.end(); ++iter)
{ {
@ -310,8 +319,8 @@ namespace MWWorld
} }
} }
} }
template <typename T> template <class T, class Id>
RecordId TypedDynamicStore<T>::read(ESM::ESMReader& reader, bool overrideOnly) RecordId TypedDynamicStore<T, Id>::read(ESM::ESMReader& reader, bool overrideOnly)
{ {
T record; T record;
bool isDeleted = false; bool isDeleted = false;
@ -321,7 +330,10 @@ namespace MWWorld
} }
insert(record, overrideOnly); insert(record, overrideOnly);
return RecordId(record.mId, isDeleted); if constexpr (std::is_same_v<Id, ESM::RefId>)
return RecordId(record.mId, isDeleted);
else
return RecordId();
} }
// LandTexture // LandTexture
@ -1232,6 +1244,6 @@ template class MWWorld::TypedDynamicStore<ESM4::Ingredient>;
template class MWWorld::TypedDynamicStore<ESM4::MiscItem>; template class MWWorld::TypedDynamicStore<ESM4::MiscItem>;
template class MWWorld::TypedDynamicStore<ESM4::Static>; template class MWWorld::TypedDynamicStore<ESM4::Static>;
template class MWWorld::TypedDynamicStore<ESM4::Light>; template class MWWorld::TypedDynamicStore<ESM4::Light>;
template class MWWorld::TypedDynamicStore<ESM4::Reference>; template class MWWorld::TypedDynamicStore<ESM4::Reference, ESM::FormId>;
template class MWWorld::TypedDynamicStore<ESM4::Cell>; template class MWWorld::TypedDynamicStore<ESM4::Cell>;
template class MWWorld::TypedDynamicStore<ESM4::Weapon>; template class MWWorld::TypedDynamicStore<ESM4::Weapon>;

View file

@ -16,6 +16,7 @@
#include <components/esm3/loadland.hpp> #include <components/esm3/loadland.hpp>
#include <components/esm3/loadpgrd.hpp> #include <components/esm3/loadpgrd.hpp>
#include <components/esm4/loadcell.hpp> #include <components/esm4/loadcell.hpp>
#include <components/esm4/loadrefr.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
@ -52,22 +53,23 @@ namespace MWWorld
{ {
}; // Empty interface to be parent of all store types }; // Empty interface to be parent of all store types
class DynamicStore : public StoreBase template <class Id>
class DynamicStoreBase : public StoreBase
{ {
public: public:
virtual ~DynamicStore() {} virtual ~DynamicStoreBase() {}
virtual void setUp() {} virtual void setUp() {}
/// List identifiers of records contained in this Store (case-smashed). No-op for Stores that don't use string /// List identifiers of records contained in this Store (case-smashed). No-op for Stores that don't use string
/// IDs. /// IDs.
virtual void listIdentifier(std::vector<ESM::RefId>& list) const {} virtual void listIdentifier(std::vector<Id>& list) const {}
virtual size_t getSize() const = 0; virtual size_t getSize() const = 0;
virtual int getDynamicSize() const { return 0; } virtual int getDynamicSize() const { return 0; }
virtual RecordId load(ESM::ESMReader& esm) = 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 clearDynamic() {}
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) const {} virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) const {}
@ -76,6 +78,8 @@ namespace MWWorld
///< Read into dynamic storage ///< Read into dynamic storage
}; };
using DynamicStore = DynamicStoreBase<ESM::RefId>;
template <class T> template <class T>
class IndexedStore : public StoreBase class IndexedStore : public StoreBase
{ {
@ -169,23 +173,23 @@ namespace MWWorld
class ESMStore; class ESMStore;
template <class T> template <class T, class Id = ESM::RefId>
class TypedDynamicStore : public DynamicStore class TypedDynamicStore : public DynamicStoreBase<Id>
{ {
typedef std::unordered_map<ESM::RefId, T> Static; typedef std::unordered_map<Id, T> Static;
Static mStatic; Static mStatic;
/// @par mShared usually preserves the record order as it came from the content files (this /// @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 /// is relevant for the spell autocalc code and selection order
/// for heads/hairs in the character creation) /// for heads/hairs in the character creation)
std::vector<T*> mShared; std::vector<T*> mShared;
typedef std::unordered_map<ESM::RefId, T> Dynamic; typedef std::unordered_map<Id, T> Dynamic;
Dynamic mDynamic; Dynamic mDynamic;
friend class ESMStore; friend class ESMStore;
public: public:
TypedDynamicStore(); TypedDynamicStore();
TypedDynamicStore(const TypedDynamicStore<T>& orig); TypedDynamicStore(const TypedDynamicStore<T, Id>& orig);
typedef SharedIterator<T> iterator; typedef SharedIterator<T> iterator;
@ -193,19 +197,19 @@ namespace MWWorld
void clearDynamic() override; void clearDynamic() override;
void setUp() override; void setUp() override;
const T* search(const ESM::RefId& id) const; const T* search(const Id& id) const;
const T* searchStatic(const ESM::RefId& id) const; const T* searchStatic(const Id& id) const;
/** /**
* Does the record with this ID come from the dynamic store? * 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. */ /** 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; const T* searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const;
// calls `search` and throws an exception if not found // 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 begin() const;
iterator end() const; iterator end() const;
@ -215,13 +219,13 @@ namespace MWWorld
int getDynamicSize() const override; int getDynamicSize() const override;
/// @note The record identifiers are listed in the order that the records were defined by the content files. /// @note The record identifiers are listed in the order that the records were defined by the content files.
void listIdentifier(std::vector<ESM::RefId>& list) const override; void listIdentifier(std::vector<Id>& list) const override;
T* insert(const T& item, bool overrideOnly = false); T* insert(const T& item, bool overrideOnly = false);
T* insertStatic(const T& item); T* insertStatic(const T& item);
bool eraseStatic(const ESM::RefId& id) override; bool eraseStatic(const Id& id) override;
bool erase(const ESM::RefId& id); bool erase(const Id& id);
bool erase(const T& item); bool erase(const T& item);
RecordId load(ESM::ESMReader& esm) override; RecordId load(ESM::ESMReader& esm) override;
@ -533,6 +537,11 @@ namespace MWWorld
const MWDialogue::KeywordSearch<std::string, int>& getDialogIdKeywordSearch() const; const MWDialogue::KeywordSearch<std::string, int>& getDialogIdKeywordSearch() const;
}; };
template <>
class Store<ESM4::Reference> : public TypedDynamicStore<ESM4::Reference, ESM::FormId>
{
};
} // end namespace } // end namespace
#endif #endif

View file

@ -18,8 +18,8 @@ namespace ESM
bool hasContentFile() const { return mContentFile >= 0; } bool hasContentFile() const { return mContentFile >= 0; }
bool isSet() const { return mIndex != 0 || mContentFile != -1; } bool isSet() const { return mIndex != 0 || mContentFile != -1; }
// Used in ESM4 as a null reference // Zero is used in ESM4 as a null reference
bool isZero() const { return mIndex == 0 && mContentFile == 0; } bool isZeroOrUnset() const { return mIndex == 0 && (mContentFile == 0 || mContentFile == -1); }
std::string toString() const; std::string toString() const;
FormId32 toUint32() const; FormId32 toUint32() const;

View file

@ -213,7 +213,7 @@ void ESM4::Land::load(ESM4::Reader& reader)
bool missing = false; bool missing = false;
for (int i = 0; i < 4; ++i) 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 << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " missing base, quad " << i << std::endl;
// std::cout << "layers " << mTextures[i].layers.size() << std::endl; // std::cout << "layers " << mTextures[i].layers.size() << std::endl;

View file

@ -34,9 +34,8 @@
void ESM4::Reference::load(ESM4::Reader& reader) void ESM4::Reference::load(ESM4::Reader& reader)
{ {
mFormId = reader.hdr().record.getFormId(); mId = reader.hdr().record.getFormId();
reader.adjustFormId(mFormId); reader.adjustFormId(mId);
mId = ESM::RefId::formIdRefId(mFormId);
mFlags = reader.hdr().record.flags; mFlags = reader.hdr().record.flags;
mParentFormId = reader.currCell(); // NOTE: only for persistent refs? mParentFormId = reader.currCell(); // NOTE: only for persistent refs?
mParent = ESM::RefId::formIdRefId(mParentFormId); mParent = ESM::RefId::formIdRefId(mParentFormId);

View file

@ -74,8 +74,7 @@ namespace ESM4
struct Reference struct Reference
{ {
FormId mFormId; // from the header FormId mId; // from the header
ESM::RefId mId;
FormId mParentFormId; // cell FormId (currently persistent refs only), from the loading sequence FormId mParentFormId; // cell FormId (currently persistent refs only), from the loading sequence
// NOTE: for exterior cells it will be the dummy cell FormId // NOTE: for exterior cells it will be the dummy cell FormId

View file

@ -1223,4 +1223,90 @@
-- @field #string id Record id -- @field #string id Record id
-- @field #string model VFS path to the model -- @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 return nil