1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 09:35:28 +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
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

View File

@ -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<ESM4::Activator>::registerSelf();
ESM4Static<ESM4::Potion>::registerSelf();
ESM4Static<ESM4::Ammunition>::registerSelf();
ESM4Static<ESM4::Armor>::registerSelf();
ESM4Static<ESM4::Book>::registerSelf();
ESM4Static<ESM4::Clothing>::registerSelf();
ESM4Static<ESM4::Container>::registerSelf();
ESM4Static<ESM4::Door>::registerSelf();
ESM4Static<ESM4::Ingredient>::registerSelf();
ESM4Static<ESM4::MiscItem>::registerSelf();
ESM4Static<ESM4::Static>::registerSelf();
ESM4Static<ESM4::Weapon>::registerSelf();
ESM4Named<ESM4::Activator>::registerSelf();
ESM4Named<ESM4::Potion>::registerSelf();
ESM4Named<ESM4::Ammunition>::registerSelf();
ESM4Named<ESM4::Armor>::registerSelf();
ESM4Named<ESM4::Book>::registerSelf();
ESM4Named<ESM4::Clothing>::registerSelf();
ESM4Named<ESM4::Container>::registerSelf();
ESM4Named<ESM4::Door>::registerSelf();
ESM4Named<ESM4::Ingredient>::registerSelf();
ESM4Named<ESM4::MiscItem>::registerSelf();
ESM4Static::registerSelf();
ESM4Named<ESM4::Weapon>::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
{
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
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
#define OPENW_MWCLASS_LIGHT4
#include "../mwworld/registeredclass.hpp"
#include "esm4base.hpp"
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();
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

View File

@ -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);
}
}

View File

@ -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 <MyGUI_TextIterator.h>
namespace MWClass
{
class Static : public MWWorld::RegisteredClass<Static>
@ -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 <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

View File

@ -158,6 +158,50 @@ namespace MWLua
case ESM::REC_REPA:
cell.mStore->template forEachType<ESM::Repair>(visitor);
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:
ok = false;
}

View File

@ -1,6 +1,7 @@
#include "types.hpp"
#include <components/esm3/loaddoor.hpp>
#include <components/esm4/loaddoor.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
@ -15,6 +16,11 @@ namespace sol
struct is_automagical<ESM::Door> : std::false_type
{
};
template <>
struct is_automagical<ESM4::Door> : 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<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 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)

View File

@ -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 <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>();
@ -75,9 +78,9 @@ namespace MWLua
// Provide the interface of a read-only array.
using StoreT = MWWorld::Store<T>;
sol::state_view& lua = context.mLua->sol();
sol::usertype<StoreT> storeT = lua.new_usertype<StoreT>(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> storeT = lua.new_usertype<StoreT>(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* {

View File

@ -3,10 +3,15 @@
#include <cassert>
#include <components/debug/debuglog.hpp>
#include <components/esm/refid.hpp>
#include <components/esm3/loadcell.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/esmstore.hpp>
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<ESM4::Reference>().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)

View File

@ -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);
}

View File

@ -188,7 +188,7 @@ namespace MWWorld
auto recordType = static_cast<ESM4::RecordTypes>(reader.hdr().record.typeId);
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))
{

View File

@ -16,8 +16,8 @@
namespace
{
// TODO: Switch to C++23 to get a working version of std::unordered_map::erase
template <class T>
bool eraseFromMap(T& map, const ESM::RefId& value)
template <class T, class Id>
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<ESM::MagicEffect>;
template class IndexedStore<ESM::Skill>;
template <typename T>
TypedDynamicStore<T>::TypedDynamicStore()
template <class T, class Id>
TypedDynamicStore<T, Id>::TypedDynamicStore()
{
}
template <typename T>
TypedDynamicStore<T>::TypedDynamicStore(const TypedDynamicStore<T>& orig)
template <class T, class Id>
TypedDynamicStore<T, Id>::TypedDynamicStore(const TypedDynamicStore<T, Id>& orig)
: mStatic(orig.mStatic)
{
}
template <typename T>
void TypedDynamicStore<T>::clearDynamic()
template <class T, class Id>
void TypedDynamicStore<T, Id>::clearDynamic()
{
// remove the dynamic part of mShared
assert(mShared.size() >= mStatic.size());
@ -115,8 +115,8 @@ namespace MWWorld
mDynamic.clear();
}
template <typename T>
const T* TypedDynamicStore<T>::search(const ESM::RefId& id) const
template <class T, class Id>
const T* TypedDynamicStore<T, Id>::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 <typename T>
const T* TypedDynamicStore<T>::searchStatic(const ESM::RefId& id) const
template <class T, class Id>
const T* TypedDynamicStore<T, Id>::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 <typename T>
bool TypedDynamicStore<T>::isDynamic(const ESM::RefId& id) const
template <class T, class Id>
bool TypedDynamicStore<T, Id>::isDynamic(const Id& id) const
{
typename Dynamic::const_iterator dit = mDynamic.find(id);
return (dit != mDynamic.end());
}
template <typename T>
const T* TypedDynamicStore<T>::searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const
template <class T, class Id>
const T* TypedDynamicStore<T, Id>::searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const
{
std::vector<const T*> 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<Id, ESM::RefId>)
{
std::vector<const T*> 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<T>::searchRandom is supported only if Id is ESM::RefId");
}
template <typename T>
const T* TypedDynamicStore<T>::find(const ESM::RefId& id) const
template <class T, class Id>
const T* TypedDynamicStore<T, Id>::find(const Id& id) const
{
const T* ptr = search(id);
if (ptr == nullptr)
@ -174,8 +179,8 @@ namespace MWWorld
}
return ptr;
}
template <typename T>
RecordId TypedDynamicStore<T>::load(ESM::ESMReader& esm)
template <class T, class Id>
RecordId TypedDynamicStore<T, Id>::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<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>
typename TypedDynamicStore<T>::iterator TypedDynamicStore<T>::begin() const
template <class T, class Id>
typename TypedDynamicStore<T, Id>::iterator TypedDynamicStore<T, Id>::begin() const
{
return mShared.begin();
}
template <typename T>
typename TypedDynamicStore<T>::iterator TypedDynamicStore<T>::end() const
template <class T, class Id>
typename TypedDynamicStore<T, Id>::iterator TypedDynamicStore<T, Id>::end() const
{
return mShared.end();
}
template <typename T>
size_t TypedDynamicStore<T>::getSize() const
template <class T, class Id>
size_t TypedDynamicStore<T, Id>::getSize() const
{
return mShared.size();
}
template <typename T>
int TypedDynamicStore<T>::getDynamicSize() const
template <class T, class Id>
int TypedDynamicStore<T, Id>::getDynamicSize() const
{
return mDynamic.size();
}
template <typename T>
void TypedDynamicStore<T>::listIdentifier(std::vector<ESM::RefId>& list) const
template <class T, class Id>
void TypedDynamicStore<T, Id>::listIdentifier(std::vector<Id>& list) const
{
list.reserve(list.size() + getSize());
typename std::vector<T*>::const_iterator it = mShared.begin();
@ -227,8 +236,8 @@ namespace MWWorld
list.push_back((*it)->mId);
}
}
template <typename T>
T* TypedDynamicStore<T>::insert(const T& item, bool overrideOnly)
template <class T, class Id>
T* TypedDynamicStore<T, Id>::insert(const T& item, bool overrideOnly)
{
if (overrideOnly)
{
@ -242,8 +251,8 @@ namespace MWWorld
mShared.push_back(ptr);
return ptr;
}
template <typename T>
T* TypedDynamicStore<T>::insertStatic(const T& item)
template <class T, class Id>
T* TypedDynamicStore<T, Id>::insertStatic(const T& item)
{
std::pair<typename Static::iterator, bool> 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 <typename T>
bool TypedDynamicStore<T>::eraseStatic(const ESM::RefId& id)
template <class T, class Id>
bool TypedDynamicStore<T, Id>::eraseStatic(const Id& id)
{
typename Static::iterator it = mStatic.find(id);
@ -277,8 +286,8 @@ namespace MWWorld
return true;
}
template <typename T>
bool TypedDynamicStore<T>::erase(const ESM::RefId& id)
template <class T, class Id>
bool TypedDynamicStore<T, Id>::erase(const Id& id)
{
if (!eraseFromMap(mDynamic, id))
return false;
@ -292,13 +301,13 @@ namespace MWWorld
}
return true;
}
template <typename T>
bool TypedDynamicStore<T>::erase(const T& item)
template <class T, class Id>
bool TypedDynamicStore<T, Id>::erase(const T& item)
{
return erase(item.mId);
}
template <typename T>
void TypedDynamicStore<T>::write(ESM::ESMWriter& writer, Loading::Listener& progress) const
template <class T, class Id>
void TypedDynamicStore<T, Id>::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 <typename T>
RecordId TypedDynamicStore<T>::read(ESM::ESMReader& reader, bool overrideOnly)
template <class T, class Id>
RecordId TypedDynamicStore<T, Id>::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<Id, ESM::RefId>)
return RecordId(record.mId, isDeleted);
else
return RecordId();
}
// LandTexture
@ -1232,6 +1244,6 @@ template class MWWorld::TypedDynamicStore<ESM4::Ingredient>;
template class MWWorld::TypedDynamicStore<ESM4::MiscItem>;
template class MWWorld::TypedDynamicStore<ESM4::Static>;
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::Weapon>;

View File

@ -16,6 +16,7 @@
#include <components/esm3/loadland.hpp>
#include <components/esm3/loadpgrd.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/esm4/loadrefr.hpp>
#include <components/misc/rng.hpp>
#include <components/misc/strings/algorithm.hpp>
@ -52,22 +53,23 @@ namespace MWWorld
{
}; // Empty interface to be parent of all store types
class DynamicStore : public StoreBase
template <class Id>
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<ESM::RefId>& list) const {}
virtual void listIdentifier(std::vector<Id>& 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<ESM::RefId>;
template <class T>
class IndexedStore : public StoreBase
{
@ -169,23 +173,23 @@ namespace MWWorld
class ESMStore;
template <class T>
class TypedDynamicStore : public DynamicStore
template <class T, class Id = ESM::RefId>
class TypedDynamicStore : public DynamicStoreBase<Id>
{
typedef std::unordered_map<ESM::RefId, T> Static;
typedef std::unordered_map<Id, T> 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<T*> mShared;
typedef std::unordered_map<ESM::RefId, T> Dynamic;
typedef std::unordered_map<Id, T> Dynamic;
Dynamic mDynamic;
friend class ESMStore;
public:
TypedDynamicStore();
TypedDynamicStore(const TypedDynamicStore<T>& orig);
TypedDynamicStore(const TypedDynamicStore<T, Id>& orig);
typedef SharedIterator<T> 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<ESM::RefId>& list) const override;
void listIdentifier(std::vector<Id>& 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<std::string, int>& getDialogIdKeywordSearch() const;
};
template <>
class Store<ESM4::Reference> : public TypedDynamicStore<ESM4::Reference, ESM::FormId>
{
};
} // end namespace
#endif

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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