1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-06 09:39:49 +00:00
OpenMW/apps/openmw/mwlua/objectbindings.cpp

274 lines
12 KiB
C++
Raw Normal View History

2020-12-18 23:21:10 +01:00
#include "luabindings.hpp"
#include <components/lua/luastate.hpp>
2021-02-17 22:56:14 +01:00
#include <components/queries/query.hpp>
2020-12-18 23:21:10 +01:00
2021-01-29 01:54:54 +01:00
#include "../mwclass/door.hpp"
2021-01-23 21:08:50 +01:00
#include "../mwworld/containerstore.hpp"
#include "../mwworld/inventorystore.hpp"
2020-12-18 23:21:10 +01:00
#include "eventqueue.hpp"
#include "luamanagerimp.hpp"
2021-01-23 21:08:50 +01:00
namespace MWLua
{
template <typename ObjectT>
struct Inventory
{
ObjectT mObj;
};
}
2020-12-18 23:21:10 +01:00
namespace sol
{
template <>
struct is_automagical<MWLua::LObject> : std::false_type {};
template <>
struct is_automagical<MWLua::GObject> : std::false_type {};
template <>
struct is_automagical<MWLua::LObjectList> : std::false_type {};
template <>
struct is_automagical<MWLua::GObjectList> : std::false_type {};
2021-01-23 21:08:50 +01:00
template <>
struct is_automagical<MWLua::Inventory<MWLua::LObject>> : std::false_type {};
template <>
struct is_automagical<MWLua::Inventory<MWLua::GObject>> : std::false_type {};
2020-12-18 23:21:10 +01:00
}
namespace MWLua
{
2021-04-17 11:50:20 +02:00
template <typename ObjT>
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
2020-12-18 23:21:10 +01:00
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)
{
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)
2020-12-18 23:21:10 +01:00
{
auto iter = [registry](const ListT& l, int64_t i) -> sol::optional<std::tuple<int64_t, ObjectT>>
{
if (i >= 0 && i < static_cast<int64_t>(l.mIds->size()))
return std::make_tuple(i + 1, ObjectT((*l.mIds)[i], registry));
else
return sol::nullopt;
};
return std::make_tuple(iter, list, 0);
};
2021-02-17 22:56:14 +01:00
listT["select"] = [context](const ListT& list, const Queries::Query& query)
{
return ListT{selectObjectsFromList(query, list.mIds, context)};
};
2020-12-18 23:21:10 +01:00
}
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();
});
2021-04-17 11:50:20 +02:00
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>>
2021-03-28 17:44:08 +02:00
{
2021-04-17 11:50:20 +02:00
const MWWorld::Ptr& ptr = o.ptr();
if (ptr.isInCell())
return Cell<ObjectT>{ptr.getCell()};
else
2021-04-23 02:49:12 +02:00
return sol::nullopt;
2021-03-28 17:44:08 +02:00
});
2020-12-18 23:21:10 +01:00
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);
2021-01-23 21:08:50 +01:00
objectT["count"] = sol::readonly_property([](const ObjectT& o) { return o.ptr().getRefData().getCount(); });
2020-12-18 23:21:10 +01:00
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()));
};
2021-04-17 11:50:20 +02:00
2020-12-18 23:21:10 +01:00
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)
2020-12-18 23:21:10 +01:00
{
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);
2020-12-18 23:21:10 +01:00
};
2021-01-23 21:08:50 +01:00
2021-11-15 00:54:01 +01:00
objectT["teleport"] = [context](const GObject& object, std::string_view cell,
const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot)
2021-01-23 21:08:50 +01:00
{
2021-03-28 17:44:08 +02:00
MWWorld::Ptr ptr = object.ptr();
osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3();
2021-11-15 00:54:01 +01:00
auto action = std::make_unique<TeleportAction>(context.mLua, object.id(), std::string(cell), pos, rot);
2021-03-28 17:44:08 +02:00
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
2021-11-15 00:54:01 +01:00
context.mLuaManager->addTeleportPlayerAction(std::move(action));
2021-03-28 17:44:08 +02:00
else
2021-11-15 00:54:01 +01:00
context.mLuaManager->addAction(std::move(action));
2021-01-23 21:08:50 +01:00
};
2020-12-18 23:21:10 +01:00
}
}
2021-01-23 21:08:50 +01:00
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");
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)
2021-01-23 21:08:50 +01:00
{
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)));
2021-01-23 21:08:50 +01:00
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);
2021-01-23 21:08:50 +01:00
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
2021-03-28 17:44:08 +02:00
// TODO
2021-01-23 21:08:50 +01:00
// obj.inventory:drop(obj2, [count])
// obj.inventory:drop(recordId, [count])
// obj.inventory:addNew(recordId, [count])
// obj.inventory:remove(obj/recordId, [count])
2021-03-28 17:44:08 +02:00
/*objectT["moveInto"] = [](const GObject& obj, const InventoryT& inventory) {};
2021-01-23 21:08:50 +01:00
inventoryT["drop"] = [](const InventoryT& inventory) {};
inventoryT["addNew"] = [](const InventoryT& inventory) {};
2021-03-28 17:44:08 +02:00
inventoryT["remove"] = [](const InventoryT& inventory) {};*/
2021-01-23 21:08:50 +01:00
}
}
2020-12-18 23:21:10 +01:00
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>());
2020-12-18 23:21:10 +01:00
addBasicBindings<ObjectT>(objectT, context);
2021-01-23 21:08:50 +01:00
addInventoryBindings<ObjectT>(objectT, prefix, context);
2020-12-18 23:21:10 +01:00
registerObjectList<ObjectT>(prefix, context);
}
void initObjectBindingsForLocalScripts(const Context& context)
{
initObjectBindings<LObject>("L", context);
}
void initObjectBindingsForGlobalScripts(const Context& context)
{
initObjectBindings<GObject>("G", context);
}
}