diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index 573188c004..5b65c3f984 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -32,6 +32,10 @@ namespace MWLua selfAPI["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); }); selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; }); selfAPI["setDirectControl"] = [](SelfObject& self, bool v) { self.mControls.controlledFromLua = v; }; + selfAPI["setEquipment"] = [](const GObject& obj, const sol::table& equipment) + { + throw std::logic_error("Not implemented"); + }; selfAPI["getCombatTarget"] = [worldView=context.mWorldView](SelfObject& self) -> sol::optional { const MWWorld::Ptr& ptr = self.ptr(); diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 8d9cbb8bee..76a01ee416 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -3,6 +3,8 @@ #include #include +#include "../mwworld/inventorystore.hpp" + #include "eventqueue.hpp" #include "worldview.hpp" @@ -19,19 +21,41 @@ namespace MWLua sol::table initCorePackage(const Context& context) { - sol::table api(context.mLua->sol(), sol::create); + auto* lua = context.mLua; + sol::table api(lua->sol(), sol::create); api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) { context.mGlobalEventQueue->push_back({std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)}); }; api["getGameTimeInSeconds"] = [world=context.mWorldView]() { return world->getGameTimeInSeconds(); }; api["getGameTimeInHours"] = [world=context.mWorldView]() { return world->getGameTimeInHours(); }; - api["OBJECT_TYPE"] = definitionList(*context.mLua, + api["OBJECT_TYPE"] = definitionList(*lua, { "Activator", "Armor", "Book", "Clothing", "Creature", "Door", "Ingredient", "Light", "Miscellaneous", "NPC", "Player", "Potion", "Static", "Weapon" }); - return context.mLua->makeReadOnly(api); + api["EQUIPMENT_SLOT"] = lua->makeReadOnly(lua->sol().create_table_with( + "Helmet", MWWorld::InventoryStore::Slot_Helmet, + "Cuirass", MWWorld::InventoryStore::Slot_Cuirass, + "Greaves", MWWorld::InventoryStore::Slot_Greaves, + "LeftPauldron", MWWorld::InventoryStore::Slot_LeftPauldron, + "RightPauldron", MWWorld::InventoryStore::Slot_RightPauldron, + "LeftGauntlet", MWWorld::InventoryStore::Slot_LeftGauntlet, + "RightGauntlet", MWWorld::InventoryStore::Slot_RightGauntlet, + "Boots", MWWorld::InventoryStore::Slot_Boots, + "Shirt", MWWorld::InventoryStore::Slot_Shirt, + "Pants", MWWorld::InventoryStore::Slot_Pants, + "Skirt", MWWorld::InventoryStore::Slot_Skirt, + "Robe", MWWorld::InventoryStore::Slot_Robe, + "LeftRing", MWWorld::InventoryStore::Slot_LeftRing, + "RightRing", MWWorld::InventoryStore::Slot_RightRing, + "Amulet", MWWorld::InventoryStore::Slot_Amulet, + "Belt", MWWorld::InventoryStore::Slot_Belt, + "CarriedRight", MWWorld::InventoryStore::Slot_CarriedRight, + "CarriedLeft", MWWorld::InventoryStore::Slot_CarriedLeft, + "Ammunition", MWWorld::InventoryStore::Slot_Ammunition + )); + return lua->makeReadOnly(api); } sol::table initWorldPackage(const Context& context) @@ -57,6 +81,7 @@ namespace MWLua // TODO: Use sqlite to search objects that are not in the scene // return GObjectList{worldView->selectObjects(query, false)}; }; + // TODO: add world.placeNewObject(recordId, cell, pos, [rot]) return context.mLua->makeReadOnly(api); } diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 8b4f9d52d3..3684521ceb 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -5,9 +5,21 @@ #include "../mwclass/door.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" + #include "eventqueue.hpp" #include "luamanagerimp.hpp" +namespace MWLua +{ + template + struct Inventory + { + ObjectT mObj; + }; +} + namespace sol { template <> @@ -18,6 +30,10 @@ namespace sol struct is_automagical : std::false_type {}; template <> struct is_automagical : std::false_type {}; + template <> + struct is_automagical> : std::false_type {}; + template <> + struct is_automagical> : std::false_type {}; } namespace MWLua @@ -88,6 +104,7 @@ namespace MWLua 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) @@ -101,6 +118,13 @@ namespace MWLua { luaManager->addLocalScript(object.ptr(), path); }; + + objectT["teleport"] = [luaManager=context.mLuaManager](const GObject& object, std::string_view cell, + const osg::Vec3f& pos, const sol::optional& rot) + { + // TODO + throw std::logic_error("Not implemented"); + }; } } @@ -127,12 +151,110 @@ namespace MWLua }); } + template + static void addInventoryBindings(sol::usertype& objectT, const std::string& prefix, const Context& context) + { + using InventoryT = Inventory; + sol::usertype inventoryT = context.mLua->sol().new_usertype(prefix + "Inventory"); + + objectT["getEquipment"] = [context](const ObjectT& o) + { + const MWWorld::Ptr& ptr = o.ptr(); + MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); + sol::table equipment(context.mLua->sol(), sol::create); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + auto it = store.getSlot(slot); + if (it == store.end()) + continue; + context.mWorldView->getObjectRegistry()->registerPtr(*it); + equipment[slot] = ObjectT(getId(*it), context.mWorldView->getObjectRegistry()); + } + return equipment; + }; + objectT["isEquipped"] = [](const ObjectT& actor, const ObjectT& item) + { + const MWWorld::Ptr& ptr = actor.ptr(); + MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); + return store.isEquipped(item.ptr()); + }; + + 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() + "]"; }; + + auto getWithMask = [context](const InventoryT& inventory, int mask) + { + const MWWorld::Ptr& ptr = inventory.mObj.ptr(); + MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); + ObjectIdList list = std::make_shared>(); + auto it = store.begin(mask); + while (it.getType() != -1) + { + const MWWorld::Ptr& item = *(it++); + context.mWorldView->getObjectRegistry()->registerPtr(item); + list->push_back(getId(item)); + } + return ObjectList{list}; + }; + + inventoryT["getAll"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_All); }; + inventoryT["getPotions"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Potion); }; + inventoryT["getApparatuses"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Apparatus); }; + inventoryT["getArmor"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Armor); }; + inventoryT["getBooks"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Book); }; + inventoryT["getClothing"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Clothing); }; + inventoryT["getIngredients"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Ingredient); }; + inventoryT["getLights"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Light); }; + inventoryT["getLockpicks"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Lockpick); }; + inventoryT["getMiscellaneous"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Miscellaneous); }; + inventoryT["getProbes"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Probe); }; + inventoryT["getRepairKits"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Repair); }; + inventoryT["getWeapons"] = + [getWithMask](const InventoryT& inventory) { return getWithMask(inventory, MWWorld::ContainerStore::Type_Weapon); }; + + 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) + { // Only for global scripts + // TODO + objectT["moveInto"] = [](const GObject& obj, const InventoryT& inventory) {}; + objectT["setEquipment"] = [](const GObject& obj, const sol::table& equipment) {}; + + // obj.inventory:drop(obj2, [count]) + // obj.inventory:drop(recordId, [count]) + // obj.inventory:addNew(recordId, [count]) + // obj.inventory:remove(obj/recordId, [count]) + inventoryT["drop"] = [](const InventoryT& inventory) {}; + inventoryT["addNew"] = [](const InventoryT& inventory) {}; + inventoryT["remove"] = [](const InventoryT& inventory) {}; + } + } + template static void initObjectBindings(const std::string& prefix, const Context& context) { sol::usertype objectT = context.mLua->sol().new_usertype(prefix + "Object"); addBasicBindings(objectT, context); addDoorBindings(objectT, context); + addInventoryBindings(objectT, prefix, context); registerObjectList(prefix, context); } @@ -148,4 +270,3 @@ namespace MWLua } } -