#include "actions.hpp" #include #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/player.hpp" namespace MWLua { void TeleportAction::apply(WorldView& worldView) const { MWWorld::CellStore* cell = worldView.findCell(mCell, mPos); if (!cell) { Log(Debug::Error) << "LuaManager::applyTeleport -> cell not found: '" << mCell << "'"; return; } MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false); const MWWorld::Class& cls = obj.getClass(); bool isPlayer = obj == world->getPlayerPtr(); if (cls.isActor()) cls.getCreatureStats(obj).land(isPlayer); if (isPlayer) { ESM::Position esmPos; static_assert(sizeof(esmPos) == sizeof(osg::Vec3f) * 2); std::memcpy(esmPos.pos, &mPos, sizeof(osg::Vec3f)); std::memcpy(esmPos.rot, &mRot, sizeof(osg::Vec3f)); world->getPlayer().setTeleported(true); if (cell->isExterior()) world->changeToExteriorCell(esmPos, true); else world->changeToInteriorCell(mCell, esmPos, true); } else { MWWorld::Ptr newObj = world->moveObject(obj, cell, mPos.x(), mPos.y(), mPos.z()); world->rotateObject(newObj, mRot.x(), mRot.y(), mRot.z()); } } void SetEquipmentAction::apply(WorldView& worldView) const { MWWorld::Ptr actor = worldView.getObjectRegistry()->getPtr(mActor, false); MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); std::array usedSlots; std::fill(usedSlots.begin(), usedSlots.end(), false); constexpr int anySlot = -1; auto tryEquipToSlot = [&actor, &store, &usedSlots, &worldView, anySlot](int slot, const Item& item) -> bool { auto old_it = slot != anySlot ? store.getSlot(slot) : store.end(); MWWorld::Ptr itemPtr; if (std::holds_alternative(item)) { itemPtr = worldView.getObjectRegistry()->getPtr(std::get(item), false); if (old_it != store.end() && *old_it == itemPtr) return true; // already equipped if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0 || itemPtr.getContainerStore() != static_cast(&store)) { Log(Debug::Warning) << "Object" << idToString(std::get(item)) << " is not in inventory"; return false; } } else { const std::string& recordId = std::get(item); if (old_it != store.end() && *old_it->getCellRef().getRefIdPtr() == recordId) return true; // already equipped itemPtr = store.search(recordId); if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0) { Log(Debug::Warning) << "There is no object with recordId='" << recordId << "' in inventory"; return false; } } auto [allowedSlots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr); bool requestedSlotIsAllowed = std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end(); if (!requestedSlotIsAllowed) { auto firstAllowed = std::find_if(allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; }); if (firstAllowed == allowedSlots.end()) { Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr); return false; } slot = *firstAllowed; } // TODO: Refactor InventoryStore to accept Ptr and get rid of this linear search. MWWorld::ContainerStoreIterator it = std::find(store.begin(), store.end(), itemPtr); if (it == store.end()) // should never happen throw std::logic_error("Item not found in container"); store.equip(slot, it, actor); return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was changed }; for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { auto old_it = store.getSlot(slot); auto new_it = mEquipment.find(slot); if (new_it == mEquipment.end()) { if (old_it != store.end()) store.unequipSlot(slot, actor); continue; } if (tryEquipToSlot(slot, new_it->second)) usedSlots[slot] = true; } for (const auto& [slot, item] : mEquipment) if (slot >= MWWorld::InventoryStore::Slots) tryEquipToSlot(anySlot, item); } }