diff --git a/apps/openmw-mp/Script/Functions/World.cpp b/apps/openmw-mp/Script/Functions/World.cpp index 1fbbf297e..ba7dba1a5 100644 --- a/apps/openmw-mp/Script/Functions/World.cpp +++ b/apps/openmw-mp/Script/Functions/World.cpp @@ -44,6 +44,11 @@ unsigned char WorldFunctions::GetEventAction() noexcept return readEvent->action; } +unsigned char WorldFunctions::GetEventContainerSubAction() noexcept +{ + return readEvent->containerSubAction; +} + const char *WorldFunctions::GetObjectRefId(unsigned int i) noexcept { return readEvent->worldObjects.at(i).refId.c_str(); @@ -287,6 +292,11 @@ void WorldFunctions::SetContainerItemEnchantmentCharge(double enchantmentCharge) tempContainerItem.enchantmentCharge = enchantmentCharge; } +void WorldFunctions::SetReceivedContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex, int actionCount) noexcept +{ + readEvent->worldObjects.at(objectIndex).containerItems.at(itemIndex).actionCount = actionCount; +} + void WorldFunctions::AddWorldObject() noexcept { tempWorldObject.droppedByPlayer = false; @@ -382,10 +392,15 @@ void WorldFunctions::SendDoorState(bool broadcast) noexcept packet->Send(true); } -void WorldFunctions::SendContainer(bool broadcast) noexcept +void WorldFunctions::SendContainer(bool broadcast, bool useLastReadEvent) noexcept { mwmp::WorldPacket *packet = mwmp::Networking::get().getWorldPacketController()->GetPacket(ID_CONTAINER); - packet->setEvent(&writeEvent); + + if (useLastReadEvent) + packet->setEvent(readEvent); + else + packet->setEvent(&writeEvent); + packet->Send(false); if (broadcast) diff --git a/apps/openmw-mp/Script/Functions/World.hpp b/apps/openmw-mp/Script/Functions/World.hpp index 7667254a5..95d8af7cd 100644 --- a/apps/openmw-mp/Script/Functions/World.hpp +++ b/apps/openmw-mp/Script/Functions/World.hpp @@ -7,6 +7,7 @@ \ {"GetObjectChangesSize", WorldFunctions::GetObjectChangesSize},\ {"GetEventAction", WorldFunctions::GetEventAction},\ + {"GetEventContainerSubAction", WorldFunctions::GetEventContainerSubAction},\ \ {"GetObjectRefId", WorldFunctions::GetObjectRefId},\ {"GetObjectRefNumIndex", WorldFunctions::GetObjectRefNumIndex},\ @@ -59,6 +60,8 @@ {"SetContainerItemCharge", WorldFunctions::SetContainerItemCharge},\ {"SetContainerItemEnchantmentCharge", WorldFunctions::SetContainerItemEnchantmentCharge},\ \ + {"SetReceivedContainerItemActionCount", WorldFunctions::SetReceivedContainerItemActionCount},\ + \ {"AddWorldObject", WorldFunctions::AddWorldObject},\ {"AddContainerItem", WorldFunctions::AddContainerItem},\ \ @@ -112,6 +115,13 @@ public: */ static unsigned char GetEventAction() noexcept; + /** + * \brief Get the container subaction type used in the read event. + * + * \return The action type (0 for NONE, 1 for DRAG, 2 for DROP, 3 for TAKE_ALL). + */ + static unsigned char GetEventContainerSubAction() noexcept; + /** * \brief Get the refId of the object at a certain index in the read event's object changes. * @@ -544,6 +554,21 @@ public: */ static void SetContainerItemEnchantmentCharge(double enchantmentCharge) noexcept; + /** + * \brief Set the action count of the container item at a certain itemIndex in the container + * changes of the object at a certain objectIndex in the read event's object changes. + * + * When resending a received Container packet, this allows you to correct the amount of items + * removed from a container by a player when it conflicts with what other players have already + * taken. + * + * \param objectIndex The index of the object. + * \param itemIndex The index of the container item. + * \param actionCount The action count. + * \return void + */ + static void SetReceivedContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex, int actionCount) noexcept; + /** * \brief Add a copy of the server's temporary world object to the server's temporary event. * @@ -653,7 +678,7 @@ public: * * \return void */ - static void SendContainer(bool broadcast = false) noexcept; + static void SendContainer(bool broadcast = false, bool useLastReadEvent = false) noexcept; /** * \brief Send a ConsoleCommand packet. diff --git a/apps/openmw-mp/processors/world/ProcessorContainer.hpp b/apps/openmw-mp/processors/world/ProcessorContainer.hpp index 8a6dcd819..19d0247f2 100644 --- a/apps/openmw-mp/processors/world/ProcessorContainer.hpp +++ b/apps/openmw-mp/processors/world/ProcessorContainer.hpp @@ -28,8 +28,10 @@ namespace mwmp if (serverCell != nullptr) serverCell->sendToLoaded(&packet, &event); } - else - packet.Send(true); + + // Otherwise, don't have any hardcoded sync and expect Lua scripts to forward + // container packets to ensure their integrity based on what exists in the + // server data Script::Call(player.getId(), event.cell.getDescription().c_str()); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 82fcde95c..5fd46f9b3 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -115,34 +115,34 @@ namespace MWGui worldEvent->reset(); worldEvent->cell = *mPtr.getCell()->getCell(); worldEvent->action = mwmp::BaseEvent::REMOVE; + worldEvent->containerSubAction = mwmp::BaseEvent::DRAG; - mwmp::WorldObject worldObject; - worldObject.refId = mPtr.getCellRef().getRefId(); - worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; - worldObject.mpNum = mPtr.getCellRef().getMpNum(); - + mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr); MWWorld::Ptr itemPtr = mModel->getItem(mSelectedItem).mBase; - - mwmp::ContainerItem containerItem; - containerItem.refId =itemPtr.getCellRef().getRefId(); - containerItem.count = itemPtr.getRefData().getCount(); - containerItem.charge = itemPtr.getCellRef().getCharge(); - containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); - containerItem.actionCount = count; - - worldObject.containerItems.push_back(containerItem); + worldEvent->addContainerItem(worldObject, itemPtr, count); worldEvent->addObject(worldObject); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i", - worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), - containerItem.refId.c_str(), containerItem.count); + worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), + itemPtr.getCellRef().getRefId().c_str(), itemPtr.getRefData().getCount()); /* End of tes3mp addition */ + /* + Start of tes3mp change (major) + + Avoid running any of the original code for dragging items, to prevent possibilities + for item duping or interaction with restricted containers + */ + return; + /* + End of tes3mp change (major) + */ + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } @@ -161,17 +161,13 @@ namespace MWGui worldEvent->reset(); worldEvent->cell = *mPtr.getCell()->getCell(); worldEvent->action = mwmp::BaseEvent::ADD; + worldEvent->containerSubAction = mwmp::BaseEvent::DROP; - mwmp::WorldObject worldObject; - worldObject.refId = mPtr.getCellRef().getRefId(); - worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; - worldObject.mpNum = mPtr.getCellRef().getMpNum(); - + mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr); MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase; - mwmp::ContainerItem containerItem; containerItem.refId = itemPtr.getCellRef().getRefId(); - + // Make sure we get the drag and drop count, not the count of the original item containerItem.count = mDragAndDrop->mDraggedCount; @@ -184,16 +180,28 @@ namespace MWGui mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); - LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i", - worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), - containerItem.refId.c_str(), containerItem.count); + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i, %i", + worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), + containerItem.refId.c_str(), containerItem.count, containerItem.charge); } /* End of tes3mp addition */ - if (success) - mDragAndDrop->drop(mModel, mItemView); + /* + Start of tes3mp change (major) + + Avoid running any of the original code for dropping items, to prevent possibilities + for item duping or interaction with restricted containers + + Instead, finish the drag in a way that removes the items in it + */ + //if (success) + // mDragAndDrop->drop(mModel, mItemView); + mDragAndDrop->finish(true); + /* + End of tes3mp change (major) + */ } void ContainerWindow::onBackgroundSelected() @@ -282,6 +290,40 @@ namespace MWGui if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) return; + /* + Start of tes3mp addition + + Send an ID_CONTAINER packet every time the Take All button is used on + a container + */ + mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent(); + worldEvent->reset(); + worldEvent->cell = *mPtr.getCell()->getCell(); + worldEvent->action = mwmp::BaseEvent::REMOVE; + worldEvent->containerSubAction = mwmp::BaseEvent::TAKE_ALL; + worldEvent->addEntireContainer(mPtr); + + mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); + mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); + + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i-%i\n- cell: %s", + mPtr.getCellRef().getRefId().c_str(), mPtr.getCellRef().getRefNum().mIndex, mPtr.getCellRef().getMpNum(), + worldEvent->cell.getDescription().c_str()); + /* + End of tes3mp addition + */ + + /* + Start of tes3mp change (major) + + Avoid running any of the original code for taking all items, to prevent + possibilities for item duping or interaction with restricted containers + */ + return; + /* + End of tes3mp change (major) + */ + // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); mModel->update(); @@ -321,32 +363,6 @@ namespace MWGui } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); - - /* - Start of tes3mp addition - - Send an ID_CONTAINER packet every time the Take All button is used on - a container - */ - mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent(); - worldEvent->reset(); - worldEvent->cell = *mPtr.getCell()->getCell(); - worldEvent->action = mwmp::BaseEvent::SET; - - mwmp::WorldObject worldObject; - worldObject.refId = mPtr.getCellRef().getRefId(); - worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; - worldObject.mpNum = mPtr.getCellRef().getMpNum(); - worldEvent->addObject(worldObject); - - mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); - mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); - - LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s", - worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str()); - /* - End of tes3mp addition - */ } void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) @@ -390,4 +406,48 @@ namespace MWGui return mModel->onTakeItem(item.mBase, count); } + /* + Start of tes3mp addition + + Make it possible to check from elsewhere whether there is currently an + item being dragged in the container window + */ + bool ContainerWindow::isOnDragAndDrop() + { + return mDragAndDrop->mIsOnDragAndDrop; + } + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to drag a specific item Ptr instead of having to rely + on an index that may have changed in the meantime, for drags that + require approval from the server + */ + bool ContainerWindow::dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount) + { + ItemModel::ModelIndex newIndex = -1; + for (unsigned int i = 0; i < mModel->getItemCount(); ++i) + { + if (mModel->getItem(i).mBase == itemPtr) + { + newIndex = i; + break; + } + } + + if (newIndex != -1) + { + mDragAndDrop->startDrag(newIndex, mSortModel, mModel, mItemView, dragCount); + return true; + } + + return false; + } + /* + End of tes3mp addition + */ } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index cf02d165d..427647fd6 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -41,6 +41,29 @@ namespace MWGui virtual void resetReference(); + /* + Start of tes3mp addition + + Make it possible to check from elsewhere whether there is currently an + item being dragged in the container window + */ + bool isOnDragAndDrop(); + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to drag a specific item Ptr instead of having to rely + on an index that may have changed in the meantime, for drags that + require approval from the server + */ + bool dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount); + /* + End of tes3mp addition + */ + private: DragAndDrop* mDragAndDrop; diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index d81b2ed00..075393dc6 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -127,10 +127,35 @@ void DragAndDrop::onFrame() finish(); } -void DragAndDrop::finish() +/* + Start of tes3mp change (minor) + + Add a deleteDragItems argument that allows the deletion of the + items in the drag as oppposed to the regular behavior of returning + them to their source model + + This is required to reduce unpredictable behavior for drags approved + or rejected by the server +*/ +void DragAndDrop::finish(bool deleteDragItems) +/* + End of tes3mp change (minor) +*/ { mIsOnDragAndDrop = false; mSourceSortModel->clearDragItems(); + + /* + Start of tes3mp addition + + Make it possible to entirely delete the items in the drag + */ + if (deleteDragItems) + mSourceModel->removeItem(mItem, mDraggedCount); + /* + End of tes3mp addition + */ + // since mSourceView doesn't get updated in drag() MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); diff --git a/apps/openmw/mwgui/draganddrop.hpp b/apps/openmw/mwgui/draganddrop.hpp index dff8cd73c..20f753fbc 100644 --- a/apps/openmw/mwgui/draganddrop.hpp +++ b/apps/openmw/mwgui/draganddrop.hpp @@ -31,7 +31,20 @@ namespace MWGui void drop (ItemModel* targetModel, ItemView* targetView); void onFrame(); - void finish(); + /* + Start of tes3mp change (minor) + + Add a deleteDragItems argument that allows the deletion of the + items in the drag as oppposed to the regular behavior of returning + them to their source model + + This is required to reduce unpredictable behavior for drags approved + or rejected by the server + */ + void finish(bool deleteDragItems = false); + /* + End of tes3mp change (minor) + */ }; } diff --git a/apps/openmw/mwmp/LocalActor.cpp b/apps/openmw/mwmp/LocalActor.cpp index ac474776e..9d86d886d 100644 --- a/apps/openmw/mwmp/LocalActor.cpp +++ b/apps/openmw/mwmp/LocalActor.cpp @@ -214,7 +214,7 @@ void LocalActor::updateEquipment(bool forceUpdate) equipmentChanged = true; item.refId = ""; item.count = 0; - item.charge = 0; + item.charge = -1; item.enchantmentCharge = -1; } } diff --git a/apps/openmw/mwmp/WorldEvent.cpp b/apps/openmw/mwmp/WorldEvent.cpp index c62aed004..5a5352805 100644 --- a/apps/openmw/mwmp/WorldEvent.cpp +++ b/apps/openmw/mwmp/WorldEvent.cpp @@ -15,6 +15,8 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwgui/container.hpp" + #include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/summoning.hpp" @@ -50,6 +52,9 @@ void WorldEvent::reset() cell.blank(); worldObjects.clear(); guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid; + + action = -1; + containerSubAction = 0; } void WorldEvent::addObject(WorldObject worldObject) @@ -59,13 +64,17 @@ void WorldEvent::addObject(WorldObject worldObject) void WorldEvent::editContainers(MWWorld::CellStore* cellStore) { + bool isLocalEvent = guid == Main::get().getLocalPlayer()->guid; + + LOG_APPEND(Log::LOG_VERBOSE, "- isLocalEvent? %s", isLocalEvent ? "true" : "false"); + WorldObject worldObject; for (unsigned int i = 0; i < worldObjectCount; i++) { worldObject = worldObjects.at(i); - //LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum); + //LOG_APPEND(Log::LOG_VERBOSE, "- container cellRef: %s %i-%i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum); @@ -74,19 +83,41 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) //LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(), // ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum()); + bool isCurrentContainer = false; + + // If we are in a container, and it happens to be this container, keep track of that + if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container)) + { + CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer; + + if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex && + currentContainer->mpNum == ptrFound.getCellRef().getMpNum()) + { + isCurrentContainer = true; + } + } + MWWorld::ContainerStore& containerStore = ptrFound.getClass().getContainerStore(ptrFound); // If we are setting the entire contents, clear the current ones if (action == BaseEvent::SET) containerStore.clear(); + bool isLocalDrop = isLocalEvent && containerSubAction == BaseEvent::DROP; + bool isLocalDrag = isLocalEvent && containerSubAction == BaseEvent::DRAG; + bool isLocalTakeAll = isLocalEvent && containerSubAction == BaseEvent::TAKE_ALL; + std::string takeAllSound = ""; + MWWorld::Ptr ownerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); for (const auto &containerItem : worldObject.containerItems) { + //LOG_APPEND(Log::LOG_VERBOSE, "-- containerItem cellRef: %s, count: %i, actionCount: %i", + // containerItem.refId.c_str(), containerItem.count, containerItem.actionCount); + if (containerItem.refId.find("$dynamic") != string::npos) continue; - if (action == BaseEvent::ADD || action == BaseEvent::SET) + if (action == BaseEvent::SET || action == BaseEvent::ADD) { // Create a ManualRef to be able to set item charge MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), containerItem.refId, 1); @@ -102,29 +133,55 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) newPtr.getCellRef().setEnchantmentCharge(containerItem.enchantmentCharge); containerStore.add(newPtr, containerItem.count, ownerPtr, true); - } - else if (action == BaseEvent::REMOVE) + } + + else if (action == BaseEvent::REMOVE && containerItem.actionCount > 0) { // We have to find the right item ourselves because ContainerStore has no method // accounting for charge - for (const auto ptr : containerStore) + for (const auto itemPtr : containerStore) { - if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), containerItem.refId)) + if (Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), containerItem.refId)) { - if (ptr.getRefData().getCount() == containerItem.count && - ptr.getCellRef().getCharge() == containerItem.charge && - ptr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge) + if (itemPtr.getCellRef().getCharge() == containerItem.charge && + itemPtr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge) { + // Store the sound of the first item in a TAKE_ALL + if (isLocalTakeAll && takeAllSound.empty()) + takeAllSound = itemPtr.getClass().getUpSoundId(itemPtr); + // Is this an actor's container? If so, unequip this item if it was equipped if (ptrFound.getClass().isActor()) { MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound); - if (invStore.isEquipped(ptr)) - invStore.unequipItemQuantity(ptr, ptrFound, containerItem.count); + if (invStore.isEquipped(itemPtr)) + invStore.unequipItemQuantity(itemPtr, ptrFound, containerItem.count); } - containerStore.remove(ptr, containerItem.actionCount, ownerPtr); + bool isResolved = false; + + if (isLocalDrag && isCurrentContainer) + { + MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow(); + + if (!containerWindow->isOnDragAndDrop()) + { + isResolved = containerWindow->dragItemByPtr(itemPtr, containerItem.actionCount); + } + } + + if (!isResolved) + { + containerStore.remove(itemPtr, containerItem.actionCount, ownerPtr); + + if (isLocalDrag || isLocalTakeAll) + { + MWWorld::Ptr ptrPlayer = MWBase::Environment::get().getWorld()->getPlayerPtr(); + MWWorld::ContainerStore &playerStore = ptrPlayer.getClass().getContainerStore(ptrPlayer); + *playerStore.add(containerItem.refId, containerItem.actionCount, ptrPlayer); + } + } } } } @@ -140,16 +197,18 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) invStore.autoEquip(ptrFound); } - // If we are in a container, and it happens to be this container, update its view - if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container)) + // If this container was open for us, update its view + if (isCurrentContainer) { - CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer; - - if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex && - currentContainer->mpNum == ptrFound.getCellRef().getMpNum()) + if (isLocalTakeAll) { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container, ptrFound); + MWBase::Environment::get().getWindowManager()->playSound(takeAllSound); + } + else + { + MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow(); + containerWindow->setPtr(ptrFound); } } } @@ -618,6 +677,41 @@ void WorldEvent::playVideo() } } +WorldObject WorldEvent::getWorldObject(const MWWorld::Ptr& ptr) +{ + mwmp::WorldObject worldObject; + worldObject.refId = ptr.getCellRef().getRefId(); + worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex; + worldObject.mpNum = ptr.getCellRef().getMpNum(); + return worldObject; +} + +void WorldEvent::addContainerItem(mwmp::WorldObject& worldObject, const MWWorld::Ptr& itemPtr, int actionCount) +{ + mwmp::ContainerItem containerItem; + containerItem.refId = itemPtr.getCellRef().getRefId(); + containerItem.count = itemPtr.getRefData().getCount(); + containerItem.charge = itemPtr.getCellRef().getCharge(); + containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); + containerItem.actionCount = actionCount; + + worldObject.containerItems.push_back(containerItem); +} + +void WorldEvent::addEntireContainer(const MWWorld::Ptr& ptr) +{ + MWWorld::ContainerStore& containerStore = ptr.getClass().getContainerStore(ptr); + + mwmp::WorldObject worldObject = getWorldObject(ptr); + + for (const auto itemPtr : containerStore) + { + addContainerItem(worldObject, itemPtr, itemPtr.getRefData().getCount()); + } + + addObject(worldObject); +} + void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer) { if (ptr.getCellRef().getRefId().find("$dynamic") != string::npos) @@ -988,7 +1082,7 @@ void WorldEvent::sendScriptGlobalShort() mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_GLOBAL_SHORT)->Send(); } -void WorldEvent::sendContainers(MWWorld::CellStore* cellStore) +void WorldEvent::sendCellContainers(MWWorld::CellStore* cellStore) { reset(); cell = *cellStore->getCell(); @@ -1007,13 +1101,7 @@ void WorldEvent::sendContainers(MWWorld::CellStore* cellStore) for (const auto itemPtr : containerStore) { - mwmp::ContainerItem containerItem; - containerItem.refId = itemPtr.getCellRef().getRefId(); - containerItem.count = itemPtr.getRefData().getCount(); - containerItem.charge = itemPtr.getCellRef().getCharge(); - containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); - - worldObject.containerItems.push_back(containerItem); + addContainerItem(worldObject, itemPtr, 0); } addObject(worldObject); diff --git a/apps/openmw/mwmp/WorldEvent.hpp b/apps/openmw/mwmp/WorldEvent.hpp index 783744b24..416a0efb8 100644 --- a/apps/openmw/mwmp/WorldEvent.hpp +++ b/apps/openmw/mwmp/WorldEvent.hpp @@ -41,6 +41,10 @@ namespace mwmp void playMusic(); void playVideo(); + WorldObject getWorldObject(const MWWorld::Ptr& ptr); + void addContainerItem(mwmp::WorldObject& worldObject, const MWWorld::Ptr& itemPtr, int actionCount); + void addEntireContainer(const MWWorld::Ptr& ptr); + void addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer = false); void addObjectSpawn(const MWWorld::Ptr& ptr); void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master); @@ -50,6 +54,7 @@ namespace mwmp void addObjectScale(const MWWorld::Ptr& ptr, float scale); void addObjectState(const MWWorld::Ptr& ptr, bool objectState); void addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode); + void addDoorState(const MWWorld::Ptr& ptr, int state); void addMusicPlay(std::string filename); void addVideoPlay(std::string filename, bool allowSkipping); @@ -74,7 +79,7 @@ namespace mwmp void sendScriptMemberShort(); void sendScriptGlobalShort(); - void sendContainers(MWWorld::CellStore* cellStore); + void sendCellContainers(MWWorld::CellStore* cellStore); private: Networking *getNetworking(); diff --git a/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp b/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp index cb2a3b4c8..fa6c5668f 100644 --- a/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp +++ b/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp @@ -17,11 +17,11 @@ namespace mwmp { BaseObjectProcessor::Do(packet, event); - LOG_APPEND(Log::LOG_VERBOSE, "- action: %i", event.action); + LOG_APPEND(Log::LOG_VERBOSE, "- action: %i, containerSubAction: %i", event.action, event.containerSubAction); // If we've received a request for information, comply with it if (event.action == mwmp::BaseEvent::REQUEST) - event.sendContainers(ptrCellStore); + event.sendCellContainers(ptrCellStore); // Otherwise, edit containers based on the information received else event.editContainers(ptrCellStore); diff --git a/components/openmw-mp/Base/BaseEvent.hpp b/components/openmw-mp/Base/BaseEvent.hpp index 0bed7eb6c..b2a66c9fd 100644 --- a/components/openmw-mp/Base/BaseEvent.hpp +++ b/components/openmw-mp/Base/BaseEvent.hpp @@ -85,6 +85,14 @@ namespace mwmp REQUEST = 3 }; + enum CONTAINER_SUBACTION + { + NONE = 0, + DRAG = 1, + DROP = 2, + TAKE_ALL = 3 + }; + RakNet::RakNetGUID guid; std::vector worldObjects; @@ -94,6 +102,7 @@ namespace mwmp std::string consoleCommand; unsigned char action; // 0 - Clear and set in entirety, 1 - Add item, 2 - Remove item, 3 - Request items + unsigned char containerSubAction; // 0 - None, 1 - Drag, 2 - Take all bool isValid; }; diff --git a/components/openmw-mp/Packets/World/PacketContainer.cpp b/components/openmw-mp/Packets/World/PacketContainer.cpp index 2e272b523..54f7a68c4 100644 --- a/components/openmw-mp/Packets/World/PacketContainer.cpp +++ b/components/openmw-mp/Packets/World/PacketContainer.cpp @@ -16,6 +16,7 @@ void PacketContainer::Packet(RakNet::BitStream *bs, bool send) return; RW(event->action, send); + RW(event->containerSubAction, send); WorldObject worldObject; for (unsigned int i = 0; i < event->worldObjectCount; i++)