1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-06 18:40:23 +00:00

Add ESM records that are needed to store Lua scripts configuration;

Use ptr.getType() (i.e. esm record names) instead of typeid(ptr.getClass()) in apps/openmw/mwlua.
This commit is contained in:
Petr Mikheev 2021-09-26 15:14:53 +02:00
parent 3006c496fc
commit 6aab246879
5 changed files with 129 additions and 53 deletions

View File

@ -1,19 +1,6 @@
#include "object.hpp"
#include "../mwclass/activator.hpp"
#include "../mwclass/armor.hpp"
#include "../mwclass/book.hpp"
#include "../mwclass/clothing.hpp"
#include "../mwclass/container.hpp"
#include "../mwclass/creature.hpp"
#include "../mwclass/door.hpp"
#include "../mwclass/ingredient.hpp"
#include "../mwclass/light.hpp"
#include "../mwclass/misc.hpp"
#include "../mwclass/npc.hpp"
#include "../mwclass/potion.hpp"
#include "../mwclass/static.hpp"
#include "../mwclass/weapon.hpp"
#include <unordered_map>
namespace MWLua
{
@ -23,28 +10,34 @@ namespace MWLua
return std::to_string(id.mIndex) + "_" + std::to_string(id.mContentFile);
}
const static std::map<std::type_index, std::string_view> classNames = {
{typeid(MWClass::Activator), "Activator"},
{typeid(MWClass::Armor), "Armor"},
{typeid(MWClass::Book), "Book"},
{typeid(MWClass::Clothing), "Clothing"},
{typeid(MWClass::Container), "Container"},
{typeid(MWClass::Creature), "Creature"},
{typeid(MWClass::Door), "Door"},
{typeid(MWClass::Ingredient), "Ingredient"},
{typeid(MWClass::Light), "Light"},
{typeid(MWClass::Miscellaneous), "Miscellaneous"},
{typeid(MWClass::Npc), "NPC"},
{typeid(MWClass::Potion), "Potion"},
{typeid(MWClass::Static), "Static"},
{typeid(MWClass::Weapon), "Weapon"},
struct LuaObjectTypeInfo
{
std::string_view mName;
ESM::LuaScriptCfg::Flags mFlag = 0;
};
std::string_view getMWClassName(const std::type_index& cls_type, std::string_view fallback)
const static std::unordered_map<ESM::RecNameInts, LuaObjectTypeInfo> luaObjectTypeInfo = {
{ESM::REC_ACTI, {"Activator", ESM::LuaScriptCfg::sActivator}},
{ESM::REC_ARMO, {"Armor", ESM::LuaScriptCfg::sArmor}},
{ESM::REC_BOOK, {"Book", ESM::LuaScriptCfg::sBook}},
{ESM::REC_CLOT, {"Clothing", ESM::LuaScriptCfg::sClothing}},
{ESM::REC_CONT, {"Container", ESM::LuaScriptCfg::sContainer}},
{ESM::REC_CREA, {"Creature", ESM::LuaScriptCfg::sCreature}},
{ESM::REC_DOOR, {"Door", ESM::LuaScriptCfg::sDoor}},
{ESM::REC_INGR, {"Ingredient", ESM::LuaScriptCfg::sIngredient}},
{ESM::REC_LIGH, {"Light", ESM::LuaScriptCfg::sLight}},
{ESM::REC_MISC, {"Miscellaneous", ESM::LuaScriptCfg::sMiscItem}},
{ESM::REC_NPC_, {"NPC", ESM::LuaScriptCfg::sNPC}},
{ESM::REC_ALCH, {"Potion", ESM::LuaScriptCfg::sPotion}},
{ESM::REC_STAT, {"Static"}},
{ESM::REC_WEAP, {"Weapon", ESM::LuaScriptCfg::sWeapon}},
};
std::string_view getLuaObjectTypeName(ESM::RecNameInts type, std::string_view fallback)
{
auto it = classNames.find(cls_type);
if (it != classNames.end())
return it->second;
auto it = luaObjectTypeInfo.find(type);
if (it != luaObjectTypeInfo.end())
return it->second.mName;
else
return fallback;
}
@ -55,13 +48,31 @@ namespace MWLua
return id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker";
}
std::string_view getMWClassName(const MWWorld::Ptr& ptr)
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr)
{
// Behaviour of this function is a part of OpenMW Lua API. We can not just return
// `ptr.getTypeDescription()` because its implementation is distributed over many files
// and can be accidentally changed. We use `ptr.getTypeDescription()` only as a fallback
// for types that are not present in `luaObjectTypeInfo` (for such types result stability
// is not necessary because they are not listed in OpenMW Lua documentation).
if (ptr.getCellRef().getRefIdRef() == "player")
return "Player";
if (isMarker(ptr))
return "Marker";
return getMWClassName(typeid(ptr.getClass()));
return getLuaObjectTypeName(static_cast<ESM::RecNameInts>(ptr.getType()), /*fallback=*/ptr.getTypeDescription());
}
ESM::LuaScriptCfg::Flags getLuaScriptFlag(const MWWorld::Ptr& ptr)
{
if (ptr.getCellRef().getRefIdRef() == "player")
return ESM::LuaScriptCfg::sPlayer;
if (isMarker(ptr))
return 0;
auto it = luaObjectTypeInfo.find(static_cast<ESM::RecNameInts>(ptr.getType()));
if (it != luaObjectTypeInfo.end())
return it->second.mFlag;
else
return 0;
}
std::string ptrToString(const MWWorld::Ptr& ptr)
@ -69,7 +80,7 @@ namespace MWLua
std::string res = "object";
res.append(idToString(getId(ptr)));
res.append(" (");
res.append(getMWClassName(ptr));
res.append(getLuaObjectTypeName(ptr));
res.append(", ");
res.append(ptr.getCellRef().getRefIdRef());
res.append(")");

View File

@ -4,6 +4,8 @@
#include <typeindex>
#include <components/esm/cellref.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/luascripts.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -19,8 +21,12 @@ namespace MWLua
std::string idToString(const ObjectId& id);
std::string ptrToString(const MWWorld::Ptr& ptr);
bool isMarker(const MWWorld::Ptr& ptr);
std::string_view getMWClassName(const std::type_index& cls_type, std::string_view fallback = "Unknown");
std::string_view getMWClassName(const MWWorld::Ptr& ptr);
std::string_view getLuaObjectTypeName(ESM::RecNameInts recordType, std::string_view fallback = "Unknown");
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr);
// Each script has a set of flags that controls to which objects the script should be
// automatically attached. This function maps each object types to one of the flags.
ESM::LuaScriptCfg::Flags getLuaScriptFlag(const MWWorld::Ptr& ptr);
// Holds a mapping ObjectId -> MWWord::Ptr.
class ObjectRegistry
@ -64,7 +70,7 @@ namespace MWLua
ObjectId id() const { return mId; }
std::string toString() const;
std::string_view type() const { return getMWClassName(ptr()); }
std::string_view type() const { return getLuaObjectTypeName(ptr()); }
// Updates and returns the underlying Ptr. Throws an exception if object is not available.
const MWWorld::Ptr& ptr() const;

View File

@ -42,13 +42,12 @@ namespace MWLua
template <typename ObjT>
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
template <class Class>
static const MWWorld::Ptr& requireClass(const MWWorld::Ptr& ptr)
static const MWWorld::Ptr& requireRecord(ESM::RecNameInts recordType, const MWWorld::Ptr& ptr)
{
if (typeid(Class) != typeid(ptr.getClass()))
if (ptr.getType() != recordType)
{
std::string msg = "Requires type '";
msg.append(getMWClassName(typeid(Class)));
msg.append(getLuaObjectTypeName(recordType));
msg.append("', but applied to ");
msg.append(ptrToString(ptr));
throw std::runtime_error(msg);
@ -189,7 +188,7 @@ namespace MWLua
template <class ObjectT>
static void addDoorBindings(sol::usertype<ObjectT>& objectT, const Context& context)
{
auto ptr = [](const ObjectT& o) -> const MWWorld::Ptr& { return requireClass<MWClass::Door>(o.ptr()); };
auto ptr = [](const ObjectT& o) -> const MWWorld::Ptr& { return requireRecord(ESM::REC_DOOR, o.ptr()); };
objectT["isTeleport"] = sol::readonly_property([ptr](const ObjectT& o)
{

View File

@ -5,13 +5,15 @@
// List of all records, that are related to Lua.
//
// Record:
// LUAM - MWLua::LuaManager
// Records:
// LUAL - LuaScriptsCfg - list of all scripts (in content files)
// LUAM - MWLua::LuaManager (in saves)
//
// Subrecords:
// LUAF - LuaScriptCfg::mFlags
// LUAW - Start of MWLua::WorldView data
// LUAE - Start of MWLua::LocalEvent or MWLua::GlobalEvent (eventName)
// LUAS - Start LuaUtil::ScriptsContainer data (scriptName)
// LUAS - VFS path to a Lua script
// LUAD - Serialized Lua variable
// LUAT - MWLua::ScriptsContainer::Timer
// LUAC - Name of a timer callback (string)
@ -37,6 +39,28 @@ std::string ESM::loadLuaBinaryData(ESMReader& esm)
return data;
}
void ESM::LuaScriptsCfg::load(ESMReader& esm)
{
while (esm.isNextSub("LUAS"))
{
std::string name = esm.getHString();
uint64_t flags;
esm.getHNT(flags, "LUAF");
std::string data = loadLuaBinaryData(esm);
mScripts.push_back({std::move(name), std::move(data), flags});
}
}
void ESM::LuaScriptsCfg::save(ESMWriter& esm) const
{
for (const LuaScriptCfg& script : mScripts)
{
esm.writeHNString("LUAS", script.mScriptPath);
esm.writeHNT("LUAF", script.mFlags);
saveLuaBinaryData(esm, script.mInitializationData);
}
}
void ESM::LuaScripts::load(ESMReader& esm)
{
while (esm.isNextSub("LUAS"))
@ -63,8 +87,7 @@ void ESM::LuaScripts::save(ESMWriter& esm) const
for (const LuaScript& script : mScripts)
{
esm.writeHNString("LUAS", script.mScriptPath);
if (!script.mData.empty())
saveLuaBinaryData(esm, script.mData);
saveLuaBinaryData(esm, script.mData);
for (const LuaTimer& timer : script.mTimers)
{
esm.startSubRecord("LUAT");

View File

@ -9,7 +9,44 @@ namespace ESM
class ESMReader;
class ESMWriter;
// Storage structure for LuaUtil::ScriptsContainer. This is not a top-level record.
// LuaScriptCfg, LuaScriptsCfg are used in content files.
struct LuaScriptCfg
{
using Flags = uint64_t;
static constexpr Flags sGlobal = 1ull << 0;
static constexpr Flags sCustom = 1ull << 1; // local; can be attached/detached by a global script
static constexpr Flags sPlayer = 1ull << 2; // auto attach to players
// auto attach for other classes:
static constexpr Flags sActivator = 1ull << 3;
static constexpr Flags sArmor = 1ull << 4;
static constexpr Flags sBook = 1ull << 5;
static constexpr Flags sClothing = 1ull << 6;
static constexpr Flags sContainer = 1ull << 7;
static constexpr Flags sCreature = 1ull << 8;
static constexpr Flags sDoor = 1ull << 9;
static constexpr Flags sIngredient = 1ull << 10;
static constexpr Flags sLight = 1ull << 11;
static constexpr Flags sMiscItem = 1ull << 12;
static constexpr Flags sNPC = 1ull << 13;
static constexpr Flags sPotion = 1ull << 14;
static constexpr Flags sWeapon = 1ull << 15;
std::string mScriptPath;
std::string mInitializationData; // Serialized Lua table. It is a binary data. Can contain '\0'.
Flags mFlags; // bitwise OR of Flags.
};
struct LuaScriptsCfg
{
std::vector<LuaScriptCfg> mScripts;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
// LuaTimer, LuaScript, LuaScripts are used in saved game files.
// Storage structure for LuaUtil::ScriptsContainer. These are not top-level records.
// Used either for global scripts or for local scripts on a specific object.
struct LuaTimer
@ -37,11 +74,11 @@ namespace ESM
{
std::vector<LuaScript> mScripts;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
// Saves binary string `data` (can contain '\0') as record LUAD.
// Saves binary string `data` (can contain '\0') as LUAD record.
void saveLuaBinaryData(ESM::ESMWriter& esm, const std::string& data);
// Loads LUAD as binary string. If next subrecord is not LUAD, then returns an empty string.