mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-10 12:39:53 +00:00
[Lua] Change behavior of obj.type
This commit is contained in:
parent
af93ebf433
commit
d251c4e2a1
@ -17,14 +17,6 @@
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
static sol::table definitionList(LuaUtil::LuaState& lua, std::initializer_list<std::string_view> values)
|
||||
{
|
||||
sol::table res(lua.sol(), sol::create);
|
||||
for (const std::string_view& v : values)
|
||||
res[v] = v;
|
||||
return LuaUtil::makeReadOnly(res);
|
||||
}
|
||||
|
||||
static void addTimeBindings(sol::table& api, const Context& context, bool global)
|
||||
{
|
||||
api["getSimulationTime"] = [world=context.mWorldView]() { return world->getSimulationTime(); };
|
||||
@ -46,52 +38,6 @@ namespace MWLua
|
||||
// api["resume"] = []() {};
|
||||
}
|
||||
|
||||
sol::table initTypesPackage(const Context& context)
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table types(lua->sol(), sol::create);
|
||||
auto addType = [&](std::string_view name, std::optional<std::string_view> base = std::nullopt) -> sol::table
|
||||
{
|
||||
sol::table t(lua->sol(), sol::create);
|
||||
sol::table ro = LuaUtil::makeReadOnly(t);
|
||||
sol::table meta = ro[sol::metatable_key];
|
||||
meta[sol::meta_function::to_string] = [name]() { return name; };
|
||||
if (base)
|
||||
{
|
||||
t[sol::metatable_key] = LuaUtil::getMutableFromReadOnly(types[*base]);
|
||||
t["baseType"] = types[*base];
|
||||
}
|
||||
types[name] = ro;
|
||||
return t;
|
||||
};
|
||||
|
||||
addActorBindings(addType("Actor"), context);
|
||||
addType("Item");
|
||||
|
||||
addType(ObjectTypeName::Creature, "Actor");
|
||||
addType(ObjectTypeName::NPC, "Actor");
|
||||
addType(ObjectTypeName::Player, ObjectTypeName::NPC);
|
||||
|
||||
addType(ObjectTypeName::Armor, "Item");
|
||||
addType(ObjectTypeName::Book, "Item");
|
||||
addType(ObjectTypeName::Clothing, "Item");
|
||||
addType(ObjectTypeName::Ingredient, "Item");
|
||||
addType(ObjectTypeName::Light, "Item");
|
||||
addType(ObjectTypeName::MiscItem, "Item");
|
||||
addType(ObjectTypeName::Potion, "Item");
|
||||
addType(ObjectTypeName::Weapon, "Item");
|
||||
addType(ObjectTypeName::Apparatus, "Item");
|
||||
addType(ObjectTypeName::Lockpick, "Item");
|
||||
addType(ObjectTypeName::Probe, "Item");
|
||||
addType(ObjectTypeName::Repair, "Item");
|
||||
|
||||
addType(ObjectTypeName::Activator);
|
||||
addDoorBindings(addType(ObjectTypeName::Door), context);
|
||||
addType(ObjectTypeName::Static);
|
||||
|
||||
return LuaUtil::makeReadOnly(types);
|
||||
}
|
||||
|
||||
sol::table initCorePackage(const Context& context)
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
@ -107,14 +53,6 @@ namespace MWLua
|
||||
context.mGlobalEventQueue->push_back({std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||
};
|
||||
addTimeBindings(api, context, false);
|
||||
api["OBJECT_TYPE"] = definitionList(*lua, // TODO: remove, use require('openmw.types') instead
|
||||
{
|
||||
ObjectTypeName::Activator, ObjectTypeName::Armor, ObjectTypeName::Book, ObjectTypeName::Clothing,
|
||||
ObjectTypeName::Creature, ObjectTypeName::Door, ObjectTypeName::Ingredient, ObjectTypeName::Light,
|
||||
ObjectTypeName::MiscItem, ObjectTypeName::NPC, ObjectTypeName::Player, ObjectTypeName::Potion,
|
||||
ObjectTypeName::Static, ObjectTypeName::Weapon, ObjectTypeName::Apparatus, ObjectTypeName::Lockpick,
|
||||
ObjectTypeName::Probe, ObjectTypeName::Repair
|
||||
});
|
||||
api["i18n"] = [i18n=context.mI18n](const std::string& context) { return i18n->getContext(context); };
|
||||
const MWWorld::Store<ESM::GameSetting>* gmst = &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
api["getGMST"] = [lua=context.mLua, gmst](const std::string& setting) -> sol::object
|
||||
|
@ -23,7 +23,6 @@ namespace MWLua
|
||||
sol::table initCorePackage(const Context&);
|
||||
sol::table initWorldPackage(const Context&);
|
||||
sol::table initQueryPackage(const Context&);
|
||||
sol::table initTypesPackage(const Context&);
|
||||
|
||||
sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "luabindings.hpp"
|
||||
#include "userdataserializer.hpp"
|
||||
#include "types/types.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
@ -86,6 +87,7 @@ namespace MWLua
|
||||
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
|
||||
mGlobalScripts.addPackage("openmw.settings", initGlobalSettingsPackage(context));
|
||||
mGlobalScripts.addPackage("openmw.storage", initGlobalStoragePackage(context, &mGlobalStorage));
|
||||
|
||||
mCameraPackage = initCameraPackage(localContext);
|
||||
mUserInterfacePackage = initUserInterfacePackage(localContext);
|
||||
mInputPackage = initInputPackage(localContext);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types/types.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
@ -10,88 +12,12 @@ namespace MWLua
|
||||
return std::to_string(id.mIndex) + "_" + std::to_string(id.mContentFile);
|
||||
}
|
||||
|
||||
struct LuaObjectTypeInfo
|
||||
{
|
||||
std::string_view mName;
|
||||
ESM::LuaScriptCfg::Flags mFlag = 0;
|
||||
};
|
||||
|
||||
const static std::unordered_map<ESM::RecNameInts, LuaObjectTypeInfo> luaObjectTypeInfo = {
|
||||
{ESM::REC_ACTI, {ObjectTypeName::Activator, ESM::LuaScriptCfg::sActivator}},
|
||||
{ESM::REC_ARMO, {ObjectTypeName::Armor, ESM::LuaScriptCfg::sArmor}},
|
||||
{ESM::REC_BOOK, {ObjectTypeName::Book, ESM::LuaScriptCfg::sBook}},
|
||||
{ESM::REC_CLOT, {ObjectTypeName::Clothing, ESM::LuaScriptCfg::sClothing}},
|
||||
{ESM::REC_CONT, {ObjectTypeName::Container, ESM::LuaScriptCfg::sContainer}},
|
||||
{ESM::REC_CREA, {ObjectTypeName::Creature, ESM::LuaScriptCfg::sCreature}},
|
||||
{ESM::REC_DOOR, {ObjectTypeName::Door, ESM::LuaScriptCfg::sDoor}},
|
||||
{ESM::REC_INGR, {ObjectTypeName::Ingredient, ESM::LuaScriptCfg::sIngredient}},
|
||||
{ESM::REC_LIGH, {ObjectTypeName::Light, ESM::LuaScriptCfg::sLight}},
|
||||
{ESM::REC_MISC, {ObjectTypeName::MiscItem, ESM::LuaScriptCfg::sMiscItem}},
|
||||
{ESM::REC_NPC_, {ObjectTypeName::NPC, ESM::LuaScriptCfg::sNPC}},
|
||||
{ESM::REC_ALCH, {ObjectTypeName::Potion, ESM::LuaScriptCfg::sPotion}},
|
||||
{ESM::REC_STAT, {ObjectTypeName::Static}},
|
||||
{ESM::REC_WEAP, {ObjectTypeName::Weapon, ESM::LuaScriptCfg::sWeapon}},
|
||||
{ESM::REC_APPA, {ObjectTypeName::Apparatus}},
|
||||
{ESM::REC_LOCK, {ObjectTypeName::Lockpick}},
|
||||
{ESM::REC_PROB, {ObjectTypeName::Probe}},
|
||||
{ESM::REC_REPA, {ObjectTypeName::Repair}},
|
||||
};
|
||||
|
||||
std::string_view getLuaObjectTypeName(ESM::RecNameInts type, std::string_view fallback)
|
||||
{
|
||||
auto it = luaObjectTypeInfo.find(type);
|
||||
if (it != luaObjectTypeInfo.end())
|
||||
return it->second.mName;
|
||||
else
|
||||
return fallback;
|
||||
}
|
||||
|
||||
bool isMarker(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::string_view id = ptr.getCellRef().getRefId();
|
||||
return id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker";
|
||||
}
|
||||
|
||||
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().getRefId() == "player")
|
||||
return ObjectTypeName::Player;
|
||||
if (isMarker(ptr))
|
||||
return "Marker";
|
||||
return getLuaObjectTypeName(static_cast<ESM::RecNameInts>(ptr.getType()), /*fallback=*/ptr.getTypeDescription());
|
||||
}
|
||||
|
||||
ESM::LuaScriptCfg::Flags getLuaScriptFlag(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getCellRef().getRefId() == "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;
|
||||
}
|
||||
|
||||
const MWWorld::Ptr& verifyType(ESM::RecNameInts recordType, const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getType() != recordType)
|
||||
{
|
||||
std::string msg = "Requires type '";
|
||||
msg.append(getLuaObjectTypeName(recordType));
|
||||
msg.append("', but applied to ");
|
||||
msg.append(ptrToString(ptr));
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
std::string ptrToString(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::string res = "object";
|
||||
|
@ -16,31 +16,6 @@
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace ObjectTypeName
|
||||
{
|
||||
// Names of object types in Lua.
|
||||
// These names are part of OpenMW Lua API.
|
||||
constexpr std::string_view Activator = "Activator";
|
||||
constexpr std::string_view Armor = "Armor";
|
||||
constexpr std::string_view Book = "Book";
|
||||
constexpr std::string_view Clothing = "Clothing";
|
||||
constexpr std::string_view Container = "Container";
|
||||
constexpr std::string_view Creature = "Creature";
|
||||
constexpr std::string_view Door = "Door";
|
||||
constexpr std::string_view Ingredient = "Ingredient";
|
||||
constexpr std::string_view Light = "Light";
|
||||
constexpr std::string_view MiscItem = "Miscellaneous";
|
||||
constexpr std::string_view NPC = "NPC";
|
||||
constexpr std::string_view Player = "Player";
|
||||
constexpr std::string_view Potion = "Potion";
|
||||
constexpr std::string_view Static = "Static";
|
||||
constexpr std::string_view Weapon = "Weapon";
|
||||
constexpr std::string_view Apparatus = "Apparatus";
|
||||
constexpr std::string_view Lockpick = "Lockpick";
|
||||
constexpr std::string_view Probe = "Probe";
|
||||
constexpr std::string_view Repair = "Repair";
|
||||
}
|
||||
|
||||
// ObjectId is a unique identifier of a game object.
|
||||
// It can change only if the order of content files was change.
|
||||
using ObjectId = ESM::RefNum;
|
||||
@ -48,13 +23,6 @@ namespace MWLua
|
||||
std::string idToString(const ObjectId& id);
|
||||
std::string ptrToString(const MWWorld::Ptr& ptr);
|
||||
bool isMarker(const MWWorld::Ptr& ptr);
|
||||
std::string_view getLuaObjectTypeName(ESM::RecNameInts recordType, std::string_view fallback = "Unknown");
|
||||
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr);
|
||||
const MWWorld::Ptr& verifyType(ESM::RecNameInts recordType, 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
|
||||
@ -98,7 +66,6 @@ namespace MWLua
|
||||
ObjectId id() const { return mId; }
|
||||
|
||||
std::string toString() const;
|
||||
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;
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "eventqueue.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
#include "types/types.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
@ -39,226 +40,248 @@ namespace sol
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
template <typename ObjT>
|
||||
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
|
||||
namespace {
|
||||
|
||||
template <class ObjectT>
|
||||
static void registerObjectList(const std::string& prefix, const Context& context)
|
||||
{
|
||||
using ListT = ObjectList<ObjectT>;
|
||||
sol::state& lua = context.mLua->sol();
|
||||
ObjectRegistry* registry = context.mWorldView->getObjectRegistry();
|
||||
sol::usertype<ListT> listT = lua.new_usertype<ListT>(prefix + "ObjectList");
|
||||
listT[sol::meta_function::to_string] =
|
||||
[](const ListT& list) { return "{" + std::to_string(list.mIds->size()) + " objects}"; };
|
||||
listT[sol::meta_function::length] = [](const ListT& list) { return list.mIds->size(); };
|
||||
listT[sol::meta_function::index] = [registry](const ListT& list, size_t index)
|
||||
template <typename ObjT>
|
||||
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
|
||||
|
||||
template <class ObjectT>
|
||||
void registerObjectList(const std::string& prefix, const Context& context)
|
||||
{
|
||||
if (index > 0 && index <= list.mIds->size())
|
||||
return ObjectT((*list.mIds)[index - 1], registry);
|
||||
else
|
||||
throw std::runtime_error("Index out of range");
|
||||
};
|
||||
listT[sol::meta_function::ipairs] = [registry](const ListT& list)
|
||||
{
|
||||
auto iter = [registry](const ListT& l, int64_t i) -> sol::optional<std::tuple<int64_t, ObjectT>>
|
||||
using ListT = ObjectList<ObjectT>;
|
||||
sol::state& lua = context.mLua->sol();
|
||||
ObjectRegistry* registry = context.mWorldView->getObjectRegistry();
|
||||
sol::usertype<ListT> listT = lua.new_usertype<ListT>(prefix + "ObjectList");
|
||||
listT[sol::meta_function::to_string] =
|
||||
[](const ListT& list) { return "{" + std::to_string(list.mIds->size()) + " objects}"; };
|
||||
listT[sol::meta_function::length] = [](const ListT& list) { return list.mIds->size(); };
|
||||
listT[sol::meta_function::index] = [registry](const ListT& list, size_t index)
|
||||
{
|
||||
if (i >= 0 && i < static_cast<int64_t>(l.mIds->size()))
|
||||
return std::make_tuple(i + 1, ObjectT((*l.mIds)[i], registry));
|
||||
if (index > 0 && index <= list.mIds->size())
|
||||
return ObjectT((*list.mIds)[index - 1], registry);
|
||||
else
|
||||
throw std::runtime_error("Index out of range");
|
||||
};
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{
|
||||
// GObject and LObject iterators are in separate branches because if they share source code
|
||||
// there is a collision in sol and only one iterator can be mapped to Lua.
|
||||
auto iter = sol::make_object(lua, [registry](const GObjectList& l, int64_t i) -> sol::optional<std::tuple<int64_t, GObject>>
|
||||
{
|
||||
if (i >= 0 && i < static_cast<int64_t>(l.mIds->size()))
|
||||
return std::make_tuple(i + 1, GObject((*l.mIds)[i], registry));
|
||||
else
|
||||
return sol::nullopt;
|
||||
});
|
||||
listT[sol::meta_function::ipairs] = [iter](const GObjectList& list) { return std::make_tuple(iter, list, 0); };
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iter = sol::make_object(lua, [registry](const LObjectList& l, int64_t i) -> sol::optional<std::tuple<int64_t, LObject>>
|
||||
{
|
||||
if (i >= 0 && i < static_cast<int64_t>(l.mIds->size()))
|
||||
return std::make_tuple(i + 1, LObject((*l.mIds)[i], registry));
|
||||
else
|
||||
return sol::nullopt;
|
||||
});
|
||||
listT[sol::meta_function::ipairs] = [iter](const LObjectList& list) { return std::make_tuple(iter, list, 0); };
|
||||
}
|
||||
listT["select"] = [context](const ListT& list, const Queries::Query& query)
|
||||
{
|
||||
return ListT{selectObjectsFromList(query, list.mIds, context)};
|
||||
};
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||
{
|
||||
objectT["isValid"] = [](const ObjectT& o) { return o.isValid(); };
|
||||
objectT["recordId"] = sol::readonly_property([](const ObjectT& o) -> std::string
|
||||
{
|
||||
return o.ptr().getCellRef().getRefId();
|
||||
});
|
||||
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>>
|
||||
{
|
||||
const MWWorld::Ptr& ptr = o.ptr();
|
||||
if (ptr.isInCell())
|
||||
return Cell<ObjectT>{ptr.getCell()};
|
||||
else
|
||||
return sol::nullopt;
|
||||
};
|
||||
return std::make_tuple(iter, list, 0);
|
||||
};
|
||||
listT["select"] = [context](const ListT& list, const Queries::Query& query)
|
||||
{
|
||||
return ListT{selectObjectsFromList(query, list.mIds, context)};
|
||||
};
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
static void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||
{
|
||||
objectT["isValid"] = [](const ObjectT& o) { return o.isValid(); };
|
||||
objectT["recordId"] = sol::readonly_property([](const ObjectT& o) -> std::string
|
||||
{
|
||||
return o.ptr().getCellRef().getRefId();
|
||||
});
|
||||
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>>
|
||||
{
|
||||
const MWWorld::Ptr& ptr = o.ptr();
|
||||
if (ptr.isInCell())
|
||||
return Cell<ObjectT>{ptr.getCell()};
|
||||
else
|
||||
return sol::nullopt;
|
||||
});
|
||||
objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
return o.ptr().getRefData().getPosition().asVec3();
|
||||
});
|
||||
objectT["rotation"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
return o.ptr().getRefData().getPosition().asRotationVec3();
|
||||
});
|
||||
objectT["type"] = sol::readonly_property(&ObjectT::type);
|
||||
objectT["count"] = sol::readonly_property([](const ObjectT& o) { return o.ptr().getRefData().getCount(); });
|
||||
objectT[sol::meta_function::equal_to] = [](const ObjectT& a, const ObjectT& b) { return a.id() == b.id(); };
|
||||
objectT[sol::meta_function::to_string] = &ObjectT::toString;
|
||||
objectT["sendEvent"] = [context](const ObjectT& dest, std::string eventName, const sol::object& eventData)
|
||||
{
|
||||
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||
};
|
||||
|
||||
objectT["activateBy"] = [context](const ObjectT& o, const ObjectT& actor)
|
||||
{
|
||||
uint32_t esmRecordType = actor.ptr().getType();
|
||||
if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_)
|
||||
throw std::runtime_error("The argument of `activateBy` must be an actor who activates the object. Got: " +
|
||||
ptrToString(actor.ptr()));
|
||||
context.mLuaManager->addAction(std::make_unique<ActivateAction>(context.mLua, o.id(), actor.id()));
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{ // Only for global scripts
|
||||
objectT["addScript"] = [lua=context.mLua, luaManager=context.mLuaManager](const GObject& object, std::string_view path)
|
||||
});
|
||||
objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
|
||||
std::optional<int> scriptId = cfg.findId(path);
|
||||
if (!scriptId)
|
||||
throw std::runtime_error("Unknown script: " + std::string(path));
|
||||
if (!(cfg[*scriptId].mFlags & ESM::LuaScriptCfg::sCustom))
|
||||
throw std::runtime_error("Script without CUSTOM tag can not be added dynamically: " + std::string(path));
|
||||
luaManager->addCustomLocalScript(object.ptr(), *scriptId);
|
||||
};
|
||||
objectT["hasScript"] = [lua=context.mLua](const GObject& object, std::string_view path)
|
||||
return o.ptr().getRefData().getPosition().asVec3();
|
||||
});
|
||||
objectT["rotation"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
|
||||
std::optional<int> scriptId = cfg.findId(path);
|
||||
if (!scriptId)
|
||||
return false;
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (localScripts)
|
||||
return localScripts->hasScript(*scriptId);
|
||||
else
|
||||
return false;
|
||||
};
|
||||
objectT["removeScript"] = [lua=context.mLua](const GObject& object, std::string_view path)
|
||||
return o.ptr().getRefData().getPosition().asRotationVec3();
|
||||
});
|
||||
|
||||
objectT["type"] = sol::readonly_property([types=getTypeToPackageTable(context.mLua->sol())](const ObjectT& o) mutable
|
||||
{
|
||||
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
|
||||
std::optional<int> scriptId = cfg.findId(path);
|
||||
if (!scriptId)
|
||||
throw std::runtime_error("Unknown script: " + std::string(path));
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (!localScripts || !localScripts->hasScript(*scriptId))
|
||||
throw std::runtime_error("There is no script " + std::string(path) + " on " + ptrToString(ptr));
|
||||
ESM::LuaScriptCfg::Flags flags = cfg[*scriptId].mFlags;
|
||||
if ((flags & (localScripts->getAutoStartMode() | ESM::LuaScriptCfg::sCustom)) != ESM::LuaScriptCfg::sCustom)
|
||||
throw std::runtime_error("Autostarted script can not be removed: " + std::string(path));
|
||||
localScripts->removeScript(*scriptId);
|
||||
return types[o.ptr().getLuaType()];
|
||||
});
|
||||
|
||||
objectT["count"] = sol::readonly_property([](const ObjectT& o) { return o.ptr().getRefData().getCount(); });
|
||||
objectT[sol::meta_function::equal_to] = [](const ObjectT& a, const ObjectT& b) { return a.id() == b.id(); };
|
||||
objectT[sol::meta_function::to_string] = &ObjectT::toString;
|
||||
objectT["sendEvent"] = [context](const ObjectT& dest, std::string eventName, const sol::object& eventData)
|
||||
{
|
||||
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||
};
|
||||
|
||||
objectT["teleport"] = [context](const GObject& object, std::string_view cell,
|
||||
const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot)
|
||||
objectT["activateBy"] = [context](const ObjectT& o, const ObjectT& actor)
|
||||
{
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3();
|
||||
auto action = std::make_unique<TeleportAction>(context.mLua, object.id(), std::string(cell), pos, rot);
|
||||
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
context.mLuaManager->addTeleportPlayerAction(std::move(action));
|
||||
else
|
||||
context.mLuaManager->addAction(std::move(action));
|
||||
uint32_t esmRecordType = actor.ptr().getType();
|
||||
if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_)
|
||||
throw std::runtime_error("The argument of `activateBy` must be an actor who activates the object. Got: " +
|
||||
ptrToString(actor.ptr()));
|
||||
context.mLuaManager->addAction(std::make_unique<ActivateAction>(context.mLua, o.id(), actor.id()));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
static void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
|
||||
{
|
||||
using InventoryT = Inventory<ObjectT>;
|
||||
sol::usertype<InventoryT> inventoryT = context.mLua->sol().new_usertype<InventoryT>(prefix + "Inventory");
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{ // Only for global scripts
|
||||
objectT["addScript"] = [lua=context.mLua, luaManager=context.mLuaManager](const GObject& object, std::string_view path)
|
||||
{
|
||||
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
|
||||
std::optional<int> scriptId = cfg.findId(path);
|
||||
if (!scriptId)
|
||||
throw std::runtime_error("Unknown script: " + std::string(path));
|
||||
if (!(cfg[*scriptId].mFlags & ESM::LuaScriptCfg::sCustom))
|
||||
throw std::runtime_error("Script without CUSTOM tag can not be added dynamically: " + std::string(path));
|
||||
luaManager->addCustomLocalScript(object.ptr(), *scriptId);
|
||||
};
|
||||
objectT["hasScript"] = [lua=context.mLua](const GObject& object, std::string_view path)
|
||||
{
|
||||
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
|
||||
std::optional<int> scriptId = cfg.findId(path);
|
||||
if (!scriptId)
|
||||
return false;
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (localScripts)
|
||||
return localScripts->hasScript(*scriptId);
|
||||
else
|
||||
return false;
|
||||
};
|
||||
objectT["removeScript"] = [lua=context.mLua](const GObject& object, std::string_view path)
|
||||
{
|
||||
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
|
||||
std::optional<int> scriptId = cfg.findId(path);
|
||||
if (!scriptId)
|
||||
throw std::runtime_error("Unknown script: " + std::string(path));
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (!localScripts || !localScripts->hasScript(*scriptId))
|
||||
throw std::runtime_error("There is no script " + std::string(path) + " on " + ptrToString(ptr));
|
||||
ESM::LuaScriptCfg::Flags flags = cfg[*scriptId].mFlags;
|
||||
if ((flags & (localScripts->getAutoStartMode() | ESM::LuaScriptCfg::sCustom)) != ESM::LuaScriptCfg::sCustom)
|
||||
throw std::runtime_error("Autostarted script can not be removed: " + std::string(path));
|
||||
localScripts->removeScript(*scriptId);
|
||||
};
|
||||
|
||||
objectT["inventory"] = sol::readonly_property([](const ObjectT& o) { return InventoryT{o}; });
|
||||
inventoryT[sol::meta_function::to_string] =
|
||||
[](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; };
|
||||
|
||||
inventoryT["getAll"] = [worldView=context.mWorldView](const InventoryT& inventory, sol::optional<std::string_view> type)
|
||||
{
|
||||
int mask;
|
||||
if (!type.has_value())
|
||||
mask = MWWorld::ContainerStore::Type_All;
|
||||
else if (*type == ObjectTypeName::Potion)
|
||||
mask = MWWorld::ContainerStore::Type_Potion;
|
||||
else if (*type == ObjectTypeName::Armor)
|
||||
mask = MWWorld::ContainerStore::Type_Armor;
|
||||
else if (*type == ObjectTypeName::Book)
|
||||
mask = MWWorld::ContainerStore::Type_Book;
|
||||
else if (*type == ObjectTypeName::Clothing)
|
||||
mask = MWWorld::ContainerStore::Type_Clothing;
|
||||
else if (*type == ObjectTypeName::Ingredient)
|
||||
mask = MWWorld::ContainerStore::Type_Ingredient;
|
||||
else if (*type == ObjectTypeName::Light)
|
||||
mask = MWWorld::ContainerStore::Type_Light;
|
||||
else if (*type == ObjectTypeName::MiscItem)
|
||||
mask = MWWorld::ContainerStore::Type_Miscellaneous;
|
||||
else if (*type == ObjectTypeName::Weapon)
|
||||
mask = MWWorld::ContainerStore::Type_Weapon;
|
||||
else if (*type == ObjectTypeName::Apparatus)
|
||||
mask = MWWorld::ContainerStore::Type_Apparatus;
|
||||
else if (*type == ObjectTypeName::Lockpick)
|
||||
mask = MWWorld::ContainerStore::Type_Lockpick;
|
||||
else if (*type == ObjectTypeName::Probe)
|
||||
mask = MWWorld::ContainerStore::Type_Probe;
|
||||
else if (*type == ObjectTypeName::Repair)
|
||||
mask = MWWorld::ContainerStore::Type_Repair;
|
||||
else
|
||||
throw std::runtime_error(std::string("inventory:getAll doesn't support type " + std::string(*type)));
|
||||
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
ObjectIdList list = std::make_shared<std::vector<ObjectId>>();
|
||||
auto it = store.begin(mask);
|
||||
while (it.getType() != -1)
|
||||
{
|
||||
const MWWorld::Ptr& item = *(it++);
|
||||
worldView->getObjectRegistry()->registerPtr(item);
|
||||
list->push_back(getId(item));
|
||||
objectT["teleport"] = [context](const GObject& object, std::string_view cell,
|
||||
const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot)
|
||||
{
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3();
|
||||
auto action = std::make_unique<TeleportAction>(context.mLua, object.id(), std::string(cell), pos, rot);
|
||||
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
context.mLuaManager->addTeleportPlayerAction(std::move(action));
|
||||
else
|
||||
context.mLuaManager->addAction(std::move(action));
|
||||
};
|
||||
}
|
||||
return ObjectList<ObjectT>{list};
|
||||
};
|
||||
|
||||
inventoryT["countOf"] = [](const InventoryT& inventory, const std::string& recordId)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
return store.count(recordId);
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{ // Only for global scripts
|
||||
// TODO
|
||||
// obj.inventory:drop(obj2, [count])
|
||||
// obj.inventory:drop(recordId, [count])
|
||||
// obj.inventory:addNew(recordId, [count])
|
||||
// obj.inventory:remove(obj/recordId, [count])
|
||||
/*objectT["moveInto"] = [](const GObject& obj, const InventoryT& inventory) {};
|
||||
inventoryT["drop"] = [](const InventoryT& inventory) {};
|
||||
inventoryT["addNew"] = [](const InventoryT& inventory) {};
|
||||
inventoryT["remove"] = [](const InventoryT& inventory) {};*/
|
||||
}
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
static void initObjectBindings(const std::string& prefix, const Context& context)
|
||||
{
|
||||
sol::usertype<ObjectT> objectT = context.mLua->sol().new_usertype<ObjectT>(
|
||||
prefix + "Object", sol::base_classes, sol::bases<Object>());
|
||||
addBasicBindings<ObjectT>(objectT, context);
|
||||
addInventoryBindings<ObjectT>(objectT, prefix, context);
|
||||
template <class ObjectT>
|
||||
void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
|
||||
{
|
||||
using InventoryT = Inventory<ObjectT>;
|
||||
sol::usertype<InventoryT> inventoryT = context.mLua->sol().new_usertype<InventoryT>(prefix + "Inventory");
|
||||
|
||||
registerObjectList<ObjectT>(prefix, context);
|
||||
}
|
||||
objectT["inventory"] = sol::readonly_property([](const ObjectT& o) { return InventoryT{o}; });
|
||||
inventoryT[sol::meta_function::to_string] =
|
||||
[](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; };
|
||||
|
||||
inventoryT["getAll"] = [worldView=context.mWorldView, ids=getPackageToTypeTable(context.mLua->sol())](
|
||||
const InventoryT& inventory, sol::optional<sol::table> type)
|
||||
{
|
||||
int mask = -1;
|
||||
sol::optional<uint32_t> typeId = sol::nullopt;
|
||||
if (type.has_value())
|
||||
typeId = ids[*type];
|
||||
else
|
||||
mask = MWWorld::ContainerStore::Type_All;
|
||||
|
||||
if (typeId.has_value())
|
||||
{
|
||||
switch (*typeId)
|
||||
{
|
||||
case ESM::REC_ALCH: mask = MWWorld::ContainerStore::Type_Potion; break;
|
||||
case ESM::REC_ARMO: mask = MWWorld::ContainerStore::Type_Armor; break;
|
||||
case ESM::REC_BOOK: mask = MWWorld::ContainerStore::Type_Book; break;
|
||||
case ESM::REC_CLOT: mask = MWWorld::ContainerStore::Type_Clothing; break;
|
||||
case ESM::REC_INGR: mask = MWWorld::ContainerStore::Type_Ingredient; break;
|
||||
case ESM::REC_LIGH: mask = MWWorld::ContainerStore::Type_Light; break;
|
||||
case ESM::REC_MISC: mask = MWWorld::ContainerStore::Type_Miscellaneous; break;
|
||||
case ESM::REC_WEAP: mask = MWWorld::ContainerStore::Type_Weapon; break;
|
||||
case ESM::REC_APPA: mask = MWWorld::ContainerStore::Type_Apparatus; break;
|
||||
case ESM::REC_LOCK: mask = MWWorld::ContainerStore::Type_Lockpick; break;
|
||||
case ESM::REC_PROB: mask = MWWorld::ContainerStore::Type_Probe; break;
|
||||
case ESM::REC_REPA: mask = MWWorld::ContainerStore::Type_Repair; break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask == -1)
|
||||
throw std::runtime_error(std::string("Incorrect type argument in inventory:getAll: " + LuaUtil::toString(*type)));
|
||||
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
ObjectIdList list = std::make_shared<std::vector<ObjectId>>();
|
||||
auto it = store.begin(mask);
|
||||
while (it.getType() != -1)
|
||||
{
|
||||
const MWWorld::Ptr& item = *(it++);
|
||||
worldView->getObjectRegistry()->registerPtr(item);
|
||||
list->push_back(getId(item));
|
||||
}
|
||||
return ObjectList<ObjectT>{list};
|
||||
};
|
||||
|
||||
inventoryT["countOf"] = [](const InventoryT& inventory, const std::string& recordId)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
return store.count(recordId);
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{ // Only for global scripts
|
||||
// TODO
|
||||
// obj.inventory:drop(obj2, [count])
|
||||
// obj.inventory:drop(recordId, [count])
|
||||
// obj.inventory:addNew(recordId, [count])
|
||||
// obj.inventory:remove(obj/recordId, [count])
|
||||
/*objectT["moveInto"] = [](const GObject& obj, const InventoryT& inventory) {};
|
||||
inventoryT["drop"] = [](const InventoryT& inventory) {};
|
||||
inventoryT["addNew"] = [](const InventoryT& inventory) {};
|
||||
inventoryT["remove"] = [](const InventoryT& inventory) {};*/
|
||||
}
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
void initObjectBindings(const std::string& prefix, const Context& context)
|
||||
{
|
||||
sol::usertype<ObjectT> objectT = context.mLua->sol().new_usertype<ObjectT>(
|
||||
prefix + "Object", sol::base_classes, sol::bases<Object>());
|
||||
addBasicBindings<ObjectT>(objectT, context);
|
||||
addInventoryBindings<ObjectT>(objectT, prefix, context);
|
||||
|
||||
registerObjectList<ObjectT>(prefix, context);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void initObjectBindingsForLocalScripts(const Context& context)
|
||||
{
|
||||
|
193
apps/openmw/mwlua/types/types.cpp
Normal file
193
apps/openmw/mwlua/types/types.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
#include "types.hpp"
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace ObjectTypeName
|
||||
{
|
||||
// Names of object types in Lua.
|
||||
// These names are part of OpenMW Lua API.
|
||||
constexpr std::string_view Actor = "Actor"; // base type for NPC, Creature, Player
|
||||
constexpr std::string_view Item = "Item"; // base type for all items
|
||||
|
||||
constexpr std::string_view Activator = "Activator";
|
||||
constexpr std::string_view Armor = "Armor";
|
||||
constexpr std::string_view Book = "Book";
|
||||
constexpr std::string_view Clothing = "Clothing";
|
||||
constexpr std::string_view Container = "Container";
|
||||
constexpr std::string_view Creature = "Creature";
|
||||
constexpr std::string_view Door = "Door";
|
||||
constexpr std::string_view Ingredient = "Ingredient";
|
||||
constexpr std::string_view Light = "Light";
|
||||
constexpr std::string_view MiscItem = "Miscellaneous";
|
||||
constexpr std::string_view NPC = "NPC";
|
||||
constexpr std::string_view Player = "Player";
|
||||
constexpr std::string_view Potion = "Potion";
|
||||
constexpr std::string_view Static = "Static";
|
||||
constexpr std::string_view Weapon = "Weapon";
|
||||
constexpr std::string_view Apparatus = "Apparatus";
|
||||
constexpr std::string_view Lockpick = "Lockpick";
|
||||
constexpr std::string_view Probe = "Probe";
|
||||
constexpr std::string_view Repair = "Repair";
|
||||
constexpr std::string_view Marker = "Marker";
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct LuaObjectTypeInfo
|
||||
{
|
||||
std::string_view mName;
|
||||
ESM::LuaScriptCfg::Flags mFlag = 0;
|
||||
};
|
||||
|
||||
const static std::unordered_map<ESM::RecNameInts, LuaObjectTypeInfo> luaObjectTypeInfo = {
|
||||
{ESM::REC_INTERNAL_PLAYER, {ObjectTypeName::Player, ESM::LuaScriptCfg::sPlayer}},
|
||||
{ESM::REC_INTERNAL_MARKER, {ObjectTypeName::Marker}},
|
||||
{ESM::REC_ACTI, {ObjectTypeName::Activator, ESM::LuaScriptCfg::sActivator}},
|
||||
{ESM::REC_ARMO, {ObjectTypeName::Armor, ESM::LuaScriptCfg::sArmor}},
|
||||
{ESM::REC_BOOK, {ObjectTypeName::Book, ESM::LuaScriptCfg::sBook}},
|
||||
{ESM::REC_CLOT, {ObjectTypeName::Clothing, ESM::LuaScriptCfg::sClothing}},
|
||||
{ESM::REC_CONT, {ObjectTypeName::Container, ESM::LuaScriptCfg::sContainer}},
|
||||
{ESM::REC_CREA, {ObjectTypeName::Creature, ESM::LuaScriptCfg::sCreature}},
|
||||
{ESM::REC_DOOR, {ObjectTypeName::Door, ESM::LuaScriptCfg::sDoor}},
|
||||
{ESM::REC_INGR, {ObjectTypeName::Ingredient, ESM::LuaScriptCfg::sIngredient}},
|
||||
{ESM::REC_LIGH, {ObjectTypeName::Light, ESM::LuaScriptCfg::sLight}},
|
||||
{ESM::REC_MISC, {ObjectTypeName::MiscItem, ESM::LuaScriptCfg::sMiscItem}},
|
||||
{ESM::REC_NPC_, {ObjectTypeName::NPC, ESM::LuaScriptCfg::sNPC}},
|
||||
{ESM::REC_ALCH, {ObjectTypeName::Potion, ESM::LuaScriptCfg::sPotion}},
|
||||
{ESM::REC_STAT, {ObjectTypeName::Static}},
|
||||
{ESM::REC_WEAP, {ObjectTypeName::Weapon, ESM::LuaScriptCfg::sWeapon}},
|
||||
{ESM::REC_APPA, {ObjectTypeName::Apparatus}},
|
||||
{ESM::REC_LOCK, {ObjectTypeName::Lockpick}},
|
||||
{ESM::REC_PROB, {ObjectTypeName::Probe}},
|
||||
{ESM::REC_REPA, {ObjectTypeName::Repair}},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
std::string_view getLuaObjectTypeName(ESM::RecNameInts type, std::string_view fallback)
|
||||
{
|
||||
auto it = luaObjectTypeInfo.find(type);
|
||||
if (it != luaObjectTypeInfo.end())
|
||||
return it->second.mName;
|
||||
else
|
||||
return fallback;
|
||||
}
|
||||
|
||||
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
return getLuaObjectTypeName(static_cast<ESM::RecNameInts>(ptr.getLuaType()), /*fallback=*/ptr.getTypeDescription());
|
||||
}
|
||||
|
||||
ESM::LuaScriptCfg::Flags getLuaScriptFlag(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
auto it = luaObjectTypeInfo.find(static_cast<ESM::RecNameInts>(ptr.getLuaType()));
|
||||
if (it != luaObjectTypeInfo.end())
|
||||
return it->second.mFlag;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const MWWorld::Ptr& verifyType(ESM::RecNameInts recordType, const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getType() != recordType)
|
||||
{
|
||||
std::string msg = "Requires type '";
|
||||
msg.append(getLuaObjectTypeName(recordType));
|
||||
msg.append("', but applied to ");
|
||||
msg.append(ptrToString(ptr));
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
sol::table getTypeToPackageTable(lua_State* L)
|
||||
{
|
||||
constexpr std::string_view key = "typeToPackage";
|
||||
sol::state_view lua(L);
|
||||
if (lua[key] == sol::nil)
|
||||
lua[key] = sol::table(lua, sol::create);
|
||||
return lua[key];
|
||||
}
|
||||
|
||||
sol::table getPackageToTypeTable(lua_State* L)
|
||||
{
|
||||
constexpr std::string_view key = "packageToType";
|
||||
sol::state_view lua(L);
|
||||
if (lua[key] == sol::nil)
|
||||
lua[key] = sol::table(lua, sol::create);
|
||||
return lua[key];
|
||||
}
|
||||
|
||||
sol::table initTypesPackage(const Context& context)
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table types(lua->sol(), sol::create);
|
||||
auto addType = [&](std::string_view name, std::vector<ESM::RecNameInts> recTypes,
|
||||
std::optional<std::string_view> base = std::nullopt) -> sol::table
|
||||
{
|
||||
sol::table t(lua->sol(), sol::create);
|
||||
sol::table ro = LuaUtil::makeReadOnly(t);
|
||||
sol::table meta = ro[sol::metatable_key];
|
||||
meta[sol::meta_function::to_string] = [name]() { return name; };
|
||||
if (base)
|
||||
{
|
||||
t["baseType"] = types[*base];
|
||||
sol::table baseMeta(lua->sol(), sol::create);
|
||||
baseMeta[sol::meta_function::index] = LuaUtil::getMutableFromReadOnly(types[*base]);
|
||||
t[sol::metatable_key] = baseMeta;
|
||||
}
|
||||
t["objectIsInstance"] = [types=recTypes](const Object& o)
|
||||
{
|
||||
unsigned int type = o.ptr().getLuaType();
|
||||
for (ESM::RecNameInts t : types)
|
||||
if (t == type)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
types[name] = ro;
|
||||
return t;
|
||||
};
|
||||
|
||||
addActorBindings(addType(ObjectTypeName::Actor, {ESM::REC_INTERNAL_PLAYER, ESM::REC_CREA, ESM::REC_NPC_}), context);
|
||||
addType(ObjectTypeName::Item, {ESM::REC_ARMO, ESM::REC_BOOK, ESM::REC_CLOT, ESM::REC_INGR,
|
||||
ESM::REC_LIGH, ESM::REC_MISC, ESM::REC_ALCH, ESM::REC_WEAP,
|
||||
ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA});
|
||||
|
||||
addType(ObjectTypeName::Creature, {ESM::REC_CREA}, ObjectTypeName::Actor);
|
||||
addType(ObjectTypeName::NPC, {ESM::REC_INTERNAL_PLAYER, ESM::REC_NPC_}, ObjectTypeName::Actor);
|
||||
addType(ObjectTypeName::Player, {ESM::REC_INTERNAL_PLAYER}, ObjectTypeName::NPC);
|
||||
|
||||
addType(ObjectTypeName::Armor, {ESM::REC_ARMO}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Book, {ESM::REC_BOOK}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Clothing, {ESM::REC_CLOT}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Ingredient, {ESM::REC_INGR}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Light, {ESM::REC_LIGH}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::MiscItem, {ESM::REC_MISC}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Potion, {ESM::REC_ALCH}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Weapon, {ESM::REC_WEAP}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Apparatus, {ESM::REC_APPA}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Lockpick, {ESM::REC_LOCK}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Probe, {ESM::REC_PROB}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Repair, {ESM::REC_REPA}, ObjectTypeName::Item);
|
||||
|
||||
addType(ObjectTypeName::Activator, {ESM::REC_ACTI});
|
||||
addType(ObjectTypeName::Container, {ESM::REC_CONT});
|
||||
addDoorBindings(addType(ObjectTypeName::Door, {ESM::REC_DOOR}), context);
|
||||
addType(ObjectTypeName::Static, {ESM::REC_STAT});
|
||||
|
||||
sol::table typeToPackage = getTypeToPackageTable(context.mLua->sol());
|
||||
sol::table packageToType = getPackageToTypeTable(context.mLua->sol());
|
||||
for (const auto& [type, v] : luaObjectTypeInfo)
|
||||
{
|
||||
sol::object t = types[v.mName];
|
||||
if (t == sol::nil)
|
||||
continue;
|
||||
typeToPackage[type] = t;
|
||||
packageToType[t] = type;
|
||||
}
|
||||
|
||||
return LuaUtil::makeReadOnly(types);
|
||||
}
|
||||
}
|
@ -3,10 +3,27 @@
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm/luascripts.hpp>
|
||||
|
||||
#include "../context.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
std::string_view getLuaObjectTypeName(ESM::RecNameInts type, std::string_view fallback = "Unknown");
|
||||
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr);
|
||||
const MWWorld::Ptr& verifyType(ESM::RecNameInts type, const MWWorld::Ptr& ptr);
|
||||
|
||||
sol::table getTypeToPackageTable(lua_State* L);
|
||||
sol::table getPackageToTypeTable(lua_State* L);
|
||||
|
||||
// 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);
|
||||
|
||||
sol::table initTypesPackage(const Context& context);
|
||||
|
||||
// used in initTypesPackage
|
||||
void addDoorBindings(sol::table door, const Context& context);
|
||||
void addActorBindings(sol::table actor, const Context& context);
|
||||
}
|
||||
|
@ -47,6 +47,23 @@ namespace MWWorld
|
||||
throw std::runtime_error("Can't get type name from an empty object.");
|
||||
}
|
||||
|
||||
// `getType()` is not exactly what we usually mean by "type" because some refids have special meaning.
|
||||
// This function handles these special refids (and by this adds some performance overhead).
|
||||
// We use this "fixed" type in Lua because we don't want to expose the weirdness of Morrowind internals to our API.
|
||||
// TODO: Implement https://gitlab.com/OpenMW/openmw/-/issues/6617 and make `getType` work the same as `getLuaType`.
|
||||
unsigned int getLuaType() const
|
||||
{
|
||||
if(mRef == nullptr)
|
||||
throw std::runtime_error("Can't get type name from an empty object.");
|
||||
std::string_view id = mRef->mRef.getRefId();
|
||||
if (id == "player")
|
||||
return ESM::REC_INTERNAL_PLAYER;
|
||||
else if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker")
|
||||
return ESM::REC_INTERNAL_MARKER;
|
||||
else
|
||||
return mRef->getType();
|
||||
}
|
||||
|
||||
std::string_view getTypeDescription() const
|
||||
{
|
||||
return mRef ? mRef->getTypeDescription() : "nullptr";
|
||||
|
@ -94,6 +94,11 @@ constexpr unsigned int fourCC(const char(&name)[len]) {
|
||||
|
||||
enum RecNameInts : unsigned int
|
||||
{
|
||||
// Special values. Can not be used in any ESM.
|
||||
// Added to this enum to guarantee that the values don't collide with any records.
|
||||
REC_INTERNAL_PLAYER = 0,
|
||||
REC_INTERNAL_MARKER = 1,
|
||||
|
||||
// format 0 / legacy
|
||||
REC_ACTI = fourCC("ACTI"),
|
||||
REC_ALCH = fourCC("ALCH"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user