#include "containerextensions.hpp" #include #include #include /* Start of tes3mp addition Include additional headers for multiplayer purposes */ #include "../mwmp/Main.hpp" #include "../mwmp/Networking.hpp" #include "../mwmp/LocalPlayer.hpp" #include "../mwmp/PlayerList.hpp" #include "../mwmp/ObjectList.hpp" #include "../mwmp/ScriptController.hpp" /* End of tes3mp addition */ #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Container { template class OpAddItem : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); if (count<0) throw std::runtime_error ("second argument for AddItem must be non-negative"); // no-op if (count == 0) return; if(::Misc::StringUtils::ciEqual(item, "gold_005") || ::Misc::StringUtils::ciEqual(item, "gold_010") || ::Misc::StringUtils::ciEqual(item, "gold_025") || ::Misc::StringUtils::ciEqual(item, "gold_100")) item = "gold_001"; /* Start of tes3mp change (major) Allow unilateral item removal on this client from client scripts and dialogue (but not console commands) to prevent infinite loops in certain mods. Otherwise, expect the server's reply to our packet to do the removal instead, except for changes to player inventories which still require the PlayerInventory to be reworked. */ unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType()); MWWorld::Ptr itemPtr; if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr() || packetOrigin != mwmp::CLIENT_CONSOLE) itemPtr = *ptr.getClass().getContainerStore(ptr).add(item, count, ptr); /* End of tes3mp change (major) */ // Spawn a messagebox (only for items added to player's inventory and if player is talking to someone) if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory std::string msgBox; std::string itemName = itemPtr.getClass().getName(itemPtr); if (count == 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage60}"); msgBox = boost::str(boost::format(msgBox) % itemName); } else { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage61}"); msgBox = boost::str(boost::format(msgBox) % count % itemName); } MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only); } /* Start of tes3mp addition Send an ID_CONTAINER packet every time an item is added to a Ptr that doesn't belong to a DedicatedPlayer */ else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && (!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr))) { mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList(); objectList->reset(); objectList->packetOrigin = packetOrigin; objectList->cell = *ptr.getCell()->getCell(); objectList->action = mwmp::BaseObjectList::ADD; objectList->containerSubAction = mwmp::BaseObjectList::NONE; mwmp::BaseObject baseObject = objectList->getBaseObject(ptr); mwmp::ContainerItem containerItem; containerItem.refId = item; containerItem.count = count; containerItem.charge = -1; containerItem.enchantmentCharge = -1; containerItem.soul = ""; baseObject.containerItems.push_back(containerItem); objectList->addObject(baseObject); objectList->sendContainer(); } /* End of tes3mp addition */ } }; template class OpGetItemCount : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if(::Misc::StringUtils::ciEqual(item, "gold_005") || ::Misc::StringUtils::ciEqual(item, "gold_010") || ::Misc::StringUtils::ciEqual(item, "gold_025") || ::Misc::StringUtils::ciEqual(item, "gold_100")) item = "gold_001"; MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); runtime.push (store.count(item)); } }; template class OpRemoveItem : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); if (count<0) throw std::runtime_error ("second argument for RemoveItem must be non-negative"); // no-op if (count == 0) return; if(::Misc::StringUtils::ciEqual(item, "gold_005") || ::Misc::StringUtils::ciEqual(item, "gold_010") || ::Misc::StringUtils::ciEqual(item, "gold_025") || ::Misc::StringUtils::ciEqual(item, "gold_100")) item = "gold_001"; MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); std::string itemName; for (MWWorld::ConstContainerStoreIterator iter(store.cbegin()); iter != store.cend(); ++iter) if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item)) itemName = iter->getClass().getName(*iter); /* Start of tes3mp change (major) Allow unilateral item removal on this client from client scripts and dialogue (but not console commands) to prevent infinite loops in certain mods. Otherwise, expect the server's reply to our packet to do the removal instead, except for changes to player inventories which still require the PlayerInventory to be reworked. */ unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType()); int numRemoved = 0; if (ptr == MWMechanics::getPlayer() || packetOrigin != mwmp::CLIENT_CONSOLE) numRemoved = store.remove(item, count, ptr); // Spawn a messagebox (only for items removed from player's inventory) if ((numRemoved > 0) && (ptr == MWMechanics::getPlayer())) { /* End of tes3mp change (major) */ // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory std::string msgBox; if(numRemoved > 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); msgBox = boost::str (boost::format(msgBox) % numRemoved % itemName); } else { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); msgBox = boost::str (boost::format(msgBox) % itemName); } MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only); } /* Start of tes3mp addition Send an ID_CONTAINER packet every time an item is removed from a Ptr that doesn't belong to a DedicatedPlayer */ else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && (!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr))) { mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList(); objectList->reset(); objectList->packetOrigin = packetOrigin; objectList->cell = *ptr.getCell()->getCell(); objectList->action = mwmp::BaseObjectList::REMOVE; objectList->containerSubAction = mwmp::BaseObjectList::NONE; mwmp::BaseObject baseObject = objectList->getBaseObject(ptr); mwmp::ContainerItem containerItem; containerItem.refId = item; containerItem.count = 0; containerItem.actionCount = count; containerItem.charge = -1; containerItem.enchantmentCharge = -1; containerItem.soul = ""; baseObject.containerItems.push_back(containerItem); objectList->addObject(baseObject); objectList->sendContainer(); } /* End of tes3mp addition */ } }; template class OpEquip : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { if (::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) break; } if (it == invStore.end()) { it = ptr.getClass().getContainerStore (ptr).add (item, 1, ptr); std::cerr << "Implicitly adding one " << item << " to container " "to fulfil requirements of Equip instruction" << std::endl; } if (ptr == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->useItem(*it, true); else { std::shared_ptr action = it->getClass().use(*it, true); action->execute(ptr, true); } } }; template class OpGetArmorType : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer location = runtime[0].mInteger; runtime.pop(); int slot; switch (location) { case 0: slot = MWWorld::InventoryStore::Slot_Helmet; break; case 1: slot = MWWorld::InventoryStore::Slot_Cuirass; break; case 2: slot = MWWorld::InventoryStore::Slot_LeftPauldron; break; case 3: slot = MWWorld::InventoryStore::Slot_RightPauldron; break; case 4: slot = MWWorld::InventoryStore::Slot_Greaves; break; case 5: slot = MWWorld::InventoryStore::Slot_Boots; break; case 6: slot = MWWorld::InventoryStore::Slot_LeftGauntlet; break; case 7: slot = MWWorld::InventoryStore::Slot_RightGauntlet; break; case 8: slot = MWWorld::InventoryStore::Slot_CarriedLeft; // shield break; case 9: slot = MWWorld::InventoryStore::Slot_LeftGauntlet; break; case 10: slot = MWWorld::InventoryStore::Slot_RightGauntlet; break; default: throw std::runtime_error ("armor index out of range"); } const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ConstContainerStoreIterator it = invStore.getSlot (slot); if (it == invStore.end() || it->getTypeName () != typeid(ESM::Armor).name()) { runtime.push(-1); return; } int skill = it->getClass().getEquipmentSkill (*it) ; if (skill == ESM::Skill::HeavyArmor) runtime.push(2); else if (skill == ESM::Skill::MediumArmor) runtime.push(1); else if (skill == ESM::Skill::LightArmor) runtime.push(0); else runtime.push(-1); } }; template class OpHasItemEquipped : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ConstContainerStoreIterator it = invStore.getSlot (slot); if (it != invStore.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) { runtime.push(1); return; } } runtime.push(0); } }; template class OpHasSoulGem : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); int count = 0; const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); for (MWWorld::ConstContainerStoreIterator it = invStore.cbegin(MWWorld::ContainerStore::Type_Miscellaneous); it != invStore.cend(); ++it) { if (::Misc::StringUtils::ciEqual(it->getCellRef().getSoul(), name)) count += it->getRefData().getCount(); } runtime.push(count); } }; template class OpGetWeaponType : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ConstContainerStoreIterator it = invStore.getSlot (MWWorld::InventoryStore::Slot_CarriedRight); if (it == invStore.end() || it->getTypeName () != typeid(ESM::Weapon).name()) { runtime.push(-1); return; } runtime.push(it->get()->mBase->mData.mType); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Container::opcodeAddItem, new OpAddItem); interpreter.installSegment5 (Compiler::Container::opcodeAddItemExplicit, new OpAddItem); interpreter.installSegment5 (Compiler::Container::opcodeGetItemCount, new OpGetItemCount); interpreter.installSegment5 (Compiler::Container::opcodeGetItemCountExplicit, new OpGetItemCount); interpreter.installSegment5 (Compiler::Container::opcodeRemoveItem, new OpRemoveItem); interpreter.installSegment5 (Compiler::Container::opcodeRemoveItemExplicit, new OpRemoveItem); interpreter.installSegment5 (Compiler::Container::opcodeEquip, new OpEquip); interpreter.installSegment5 (Compiler::Container::opcodeEquipExplicit, new OpEquip); interpreter.installSegment5 (Compiler::Container::opcodeGetArmorType, new OpGetArmorType); interpreter.installSegment5 (Compiler::Container::opcodeGetArmorTypeExplicit, new OpGetArmorType); interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquipped, new OpHasItemEquipped); interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquippedExplicit, new OpHasItemEquipped); interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGem, new OpHasSoulGem); interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGemExplicit, new OpHasSoulGem); interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponType, new OpGetWeaponType); interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponTypeExplicit, new OpGetWeaponType); } } }