From 40e9b2d707f432616717d296aa38876440c52781 Mon Sep 17 00:00:00 2001 From: Kuyondo Date: Tue, 27 May 2025 01:48:58 +0800 Subject: [PATCH 1/3] play down sound when equip fails --- apps/openmw/mwgui/inventorywindow.cpp | 63 +++++++++++---------------- apps/openmw/mwgui/quickkeysmenu.cpp | 32 +++++++++----- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 410ed31a66..017616295f 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -522,33 +522,15 @@ namespace MWGui } MWWorld::Ptr player = MWMechanics::getPlayer(); + bool canUse = true; - // early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that - // case - if (!ptr.getClass().getEquipmentSlots(ptr).first.empty()) - { - if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}"); - updateItemView(); - return; - } - - if (!force) - { - auto canEquip = ptr.getClass().canBeEquipped(ptr, player); - - if (canEquip.first == 0) - { - MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); - updateItemView(); - return; - } - } - } + // We don't want to set OnPcEquip for items that need to be equipped; but cannot be equipped; + if (!ptr.getClass().getEquipmentSlots(ptr).first.empty() + && ptr.getClass().canBeEquipped(ptr, player).first == 0) + canUse = force && ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() != 0; // If the item has a script, set OnPCEquip or PCSkipEquip to 1 - if (!script.empty()) + if (!script.empty() && canUse) { // Ingredients, books and repair hammers must not have OnPCEquip set to 1 here auto type = ptr.getType(); @@ -561,7 +543,25 @@ namespace MWGui } std::unique_ptr action = ptr.getClass().use(ptr, force); - action->execute(player); + + action->execute(player, !canUse); + + if (mDragAndDrop->mIsOnDragAndDrop && mDragAndDrop->mItem.mBase == ptr) + { + if (canUse) + { + mDragAndDrop->finish(); + // If item is ingredient or potion don't stop drag and drop + if ((ptr.getType() == ESM::Potion::sRecordId || ptr.getType() == ESM::Ingredient::sRecordId) + && mDragAndDrop->mDraggedCount > 1) + { + mSelectedItem = getModel()->getIndex(mDragAndDrop->mItem); + dragItem(nullptr, mDragAndDrop->mDraggedCount - 1); + } + } + else + mDragAndDrop->drop(mTradeModel, mItemView); + } // Handles partial equipping (final part) if (mEquippedStackableCount.has_value()) @@ -592,8 +592,6 @@ namespace MWGui { MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase; - mDragAndDrop->finish(); - if (mDragAndDrop->mSourceModel != mTradeModel) { // Move item to the player's inventory @@ -617,17 +615,6 @@ namespace MWGui } MWBase::Environment::get().getLuaManager()->useItem(ptr, MWMechanics::getPlayer(), false); - - // If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1 - // item - if ((ptr.getType() == ESM::Potion::sRecordId || ptr.getType() == ESM::Ingredient::sRecordId) - && mDragAndDrop->mDraggedCount > 1) - { - // Item can be provided from other window for example container. - // But after DragAndDrop::startDrag item automaticly always gets to player inventory. - mSelectedItem = getModel()->getIndex(mDragAndDrop->mItem); - dragItem(nullptr, mDragAndDrop->mDraggedCount - 1); - } } else { diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index c8932c97b6..deb844078b 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -84,9 +84,8 @@ namespace MWGui case ESM::QuickKeys::Type::MagicItem: { MWWorld::Ptr item = *mKey[index].button->getUserData(); - // Make sure the item is available and is not broken - if (item.isEmpty() || item.getCellRef().getCount() < 1 - || (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0)) + // Make sure the item is available + if (item.isEmpty() || item.getCellRef().getCount() < 1) { // Try searching for a compatible replacement item = store.findReplacement(mKey[index].id); @@ -382,18 +381,29 @@ namespace MWGui if (it == store.end()) item = nullptr; - // check the item is available and not broken - if (item.isEmpty() || item.getCellRef().getCount() < 1 - || (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0)) + // check the quickkey item is available + if (item.isEmpty() || item.getCellRef().getCount() < 1) { - item = store.findReplacement(key->id); + MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + key->name); + return; + } - if (item.isEmpty() || item.getCellRef().getCount() < 1) + // check the quickkey item is not broken + if (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0) + { + const std::vector& equipmentSlots = item.getClass().getEquipmentSlots(item).first; + if (!equipmentSlots.empty()) { - MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + key->name); - - return; + const auto& itSlot = store.getSlot(equipmentSlots.front()); + // Morrowind.exe behaviour: + // Only display item broken message if; + // no item in the to-be-equipped slot + // or broken quickkey item and currently equipped item id is different + // It doesn't find a replacement + if (itSlot == store.end() || (item.getCellRef().getRefId() != itSlot->getCellRef().getRefId())) + MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}"); } + return; } if (key->type == ESM::QuickKeys::Type::Item) From e4f0723a90efe1a2661dea6fc0db2d0c1ead1a1b Mon Sep 17 00:00:00 2001 From: Kuyondo Date: Tue, 27 May 2025 15:50:23 +0800 Subject: [PATCH 2/3] cleanup --- apps/openmw/mwclass/weapon.cpp | 6 +-- apps/openmw/mwgui/inventorywindow.cpp | 62 ++++++++++++++++----------- apps/openmw/mwgui/quickkeysmenu.cpp | 9 ++-- apps/openmw/mwworld/actionequip.cpp | 9 ++-- 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 089c8c2894..3524446b26 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -270,14 +270,14 @@ namespace MWClass std::pair Weapon::canBeEquipped(const MWWorld::ConstPtr& ptr, const MWWorld::Ptr& npc) const { - if (hasItemHealth(ptr) && getItemHealth(ptr) == 0) - return { 0, "#{sInventoryMessage1}" }; - // Do not allow equip weapons from inventory during attack if (npc.isInCell() && MWBase::Environment::get().getWindowManager()->isGuiMode() && MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)) return { 0, "#{sCantEquipWeapWarning}" }; + if (hasItemHealth(ptr) && getItemHealth(ptr) == 0) + return { 0, "#{sInventoryMessage1}" }; + std::pair, bool> slots_ = getEquipmentSlots(ptr); if (slots_.first.empty()) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 017616295f..1f6b6fdeb5 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -522,18 +522,25 @@ namespace MWGui } MWWorld::Ptr player = MWMechanics::getPlayer(); - bool canUse = true; + auto type = ptr.getType(); + bool isWeaponOrArmor = type == ESM::Weapon::sRecordId || type == ESM::Armor::sRecordId; + bool isBroken = ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0; - // We don't want to set OnPcEquip for items that need to be equipped; but cannot be equipped; - if (!ptr.getClass().getEquipmentSlots(ptr).first.empty() - && ptr.getClass().canBeEquipped(ptr, player).first == 0) - canUse = force && ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() != 0; + // In vanilla, broken armor or weapons cannot be equipped + // tools with 0 charges is equippable + if (isBroken && isWeaponOrArmor) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}"); + return; + } + + bool canEquip = ptr.getClass().canBeEquipped(ptr, mPtr).first != 0; + bool shouldSetOnPcEquip = canEquip || force; // If the item has a script, set OnPCEquip or PCSkipEquip to 1 - if (!script.empty() && canUse) + if (!script.empty() && shouldSetOnPcEquip) { // Ingredients, books and repair hammers must not have OnPCEquip set to 1 here - auto type = ptr.getType(); bool isBook = type == ESM::Book::sRecordId; if (!isBook && type != ESM::Ingredient::sRecordId && type != ESM::Repair::sRecordId) ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); @@ -543,25 +550,7 @@ namespace MWGui } std::unique_ptr action = ptr.getClass().use(ptr, force); - - action->execute(player, !canUse); - - if (mDragAndDrop->mIsOnDragAndDrop && mDragAndDrop->mItem.mBase == ptr) - { - if (canUse) - { - mDragAndDrop->finish(); - // If item is ingredient or potion don't stop drag and drop - if ((ptr.getType() == ESM::Potion::sRecordId || ptr.getType() == ESM::Ingredient::sRecordId) - && mDragAndDrop->mDraggedCount > 1) - { - mSelectedItem = getModel()->getIndex(mDragAndDrop->mItem); - dragItem(nullptr, mDragAndDrop->mDraggedCount - 1); - } - } - else - mDragAndDrop->drop(mTradeModel, mItemView); - } + action->execute(player); // Handles partial equipping (final part) if (mEquippedStackableCount.has_value()) @@ -592,6 +581,16 @@ namespace MWGui { MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase; + auto canEquip = ptr.getClass().canBeEquipped(ptr, mPtr); + if (canEquip.first == 0) // cannot equip + { + mDragAndDrop->drop(mTradeModel, mItemView); // also plays down sound + MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); + return; + } + + mDragAndDrop->finish(); + if (mDragAndDrop->mSourceModel != mTradeModel) { // Move item to the player's inventory @@ -615,6 +614,17 @@ namespace MWGui } MWBase::Environment::get().getLuaManager()->useItem(ptr, MWMechanics::getPlayer(), false); + + // If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1 + // item + if ((ptr.getType() == ESM::Potion::sRecordId || ptr.getType() == ESM::Ingredient::sRecordId) + && mDragAndDrop->mDraggedCount > 1) + { + // Item can be provided from other window for example container. + // But after DragAndDrop::startDrag item automaticly always gets to player inventory. + mSelectedItem = getModel()->getIndex(mDragAndDrop->mItem); + dragItem(nullptr, mDragAndDrop->mDraggedCount - 1); + } } else { diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index deb844078b..5f3cd17732 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -395,11 +395,10 @@ namespace MWGui if (!equipmentSlots.empty()) { const auto& itSlot = store.getSlot(equipmentSlots.front()); - // Morrowind.exe behaviour: - // Only display item broken message if; - // no item in the to-be-equipped slot - // or broken quickkey item and currently equipped item id is different - // It doesn't find a replacement + // Morrowind.exe behavior: + // Only display the "item is broken" message if: + // - There is no item in the target equipment slot, or + // - The quickkey item is broken and the currently equipped item has a different ID if (itSlot == store.end() || (item.getCellRef().getRefId() != itSlot->getCellRef().getRefId())) MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}"); } diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 58e6f013aa..dbc548f585 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -21,12 +21,11 @@ namespace MWWorld MWWorld::Ptr object = getTarget(); MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); - if (object.getClass().hasItemHealth(object) && object.getCellRef().getCharge() == 0) + if (actor != MWMechanics::getPlayer()) { - if (actor == MWMechanics::getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}"); - - return; + // player logic is handled in InventoryWindow::useItem + if (object.getClass().hasItemHealth(object) && object.getCellRef().getCharge() == 0) + return; } if (!mForce) From ede768532c04d81f4e6e74019820b32cb6bc5657 Mon Sep 17 00:00:00 2001 From: Kuyondo Date: Tue, 8 Jul 2025 02:58:44 +0800 Subject: [PATCH 3/3] cleanup 2 --- apps/openmw/mwgui/inventorywindow.cpp | 8 ++++---- apps/openmw/mwgui/quickkeysmenu.cpp | 19 +------------------ apps/openmw/mwworld/inventorystore.cpp | 10 ++++++++++ apps/openmw/mwworld/inventorystore.hpp | 1 + 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 1f6b6fdeb5..acc1dd6068 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -550,7 +550,7 @@ namespace MWGui } std::unique_ptr action = ptr.getClass().use(ptr, force); - action->execute(player); + action->execute(player, !canEquip); // Handles partial equipping (final part) if (mEquippedStackableCount.has_value()) @@ -581,11 +581,11 @@ namespace MWGui { MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase; - auto canEquip = ptr.getClass().canBeEquipped(ptr, mPtr); - if (canEquip.first == 0) // cannot equip + auto [canEquipRes, canEquipMsg] = ptr.getClass().canBeEquipped(ptr, mPtr); + if (canEquipRes == 0) // cannot equip { mDragAndDrop->drop(mTradeModel, mItemView); // also plays down sound - MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); + MWBase::Environment::get().getWindowManager()->messageBox(canEquipMsg); return; } diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 5f3cd17732..191a2d8b3a 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -388,26 +388,9 @@ namespace MWGui return; } - // check the quickkey item is not broken - if (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0) - { - const std::vector& equipmentSlots = item.getClass().getEquipmentSlots(item).first; - if (!equipmentSlots.empty()) - { - const auto& itSlot = store.getSlot(equipmentSlots.front()); - // Morrowind.exe behavior: - // Only display the "item is broken" message if: - // - There is no item in the target equipment slot, or - // - The quickkey item is broken and the currently equipped item has a different ID - if (itSlot == store.end() || (item.getCellRef().getRefId() != itSlot->getCellRef().getRefId())) - MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}"); - } - return; - } - if (key->type == ESM::QuickKeys::Type::Item) { - if (!store.isEquipped(item)) + if (!store.isEquipped(item.getCellRef().getRefId())) MWBase::Environment::get().getWindowManager()->useItem(item); MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index f48f4e6e31..9143777531 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -754,6 +754,16 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::ConstPtr& item) return false; } +bool MWWorld::InventoryStore::isEquipped(const ESM::RefId& id) +{ + for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i) + { + if (getSlot(i) != end() && getSlot(i)->getCellRef().getRefId() == id) + return true; + } + return false; +} + bool MWWorld::InventoryStore::isFirstEquip() { bool first = mFirstAutoEquip; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 0af6ee2b28..d0839dd1ed 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -118,6 +118,7 @@ namespace MWWorld ///< \warning \a iterator can not be an end()-iterator, use unequip function instead bool isEquipped(const MWWorld::ConstPtr& item); + bool isEquipped(const ESM::RefId& id); ///< Utility function, returns true if the given item is equipped in any slot void setSelectedEnchantItem(const ContainerStoreIterator& iterator);