From 8817f802cb643e288f69302154bc3f51285562ce Mon Sep 17 00:00:00 2001 From: myrix Date: Sun, 24 Jul 2022 23:02:26 +0300 Subject: [PATCH 1/4] message when using empty soul gems, using preferentially filled ones with equip command --- apps/openmw/mwclass/misc.cpp | 19 +++++++++++ apps/openmw/mwclass/misc.hpp | 4 +++ apps/openmw/mwgui/inventorywindow.cpp | 13 ++++++++ apps/openmw/mwscript/containerextensions.cpp | 33 ++++++++++++++++++-- components/misc/stringops.hpp | 28 +++++++++++++++++ 5 files changed, 94 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index be5cf9c143..53c3bc4bcb 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -201,6 +201,25 @@ namespace MWClass return newPtr; } + std::pair Miscellaneous::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const + { + // Special checks for soul gems. + + if (::Misc::StringUtils::ciEqualPrefix("misc_soulgem", ptr.getCellRef().getRefId())) + { + if (npc != MWBase::Environment::get().getWorld()->getPlayerPtr()) + return std::make_pair(0, "Only the player can use soul gems."); + + if (ptr.getCellRef().getSoul().empty()) + return std::make_pair(0, "#{sNotifyMessage32}"); + + if (!MWBase::Environment::get().getWorld()->getStore().get().search(ptr.getCellRef().getSoul())) + return std::make_pair(0, "Unknown soul creature id."); + } + + return std::make_pair(1, ""); + } + std::unique_ptr Miscellaneous::use (const MWWorld::Ptr& ptr, bool force) const { if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get().search(ptr.getCellRef().getSoul())) diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index ebf38b10be..95d861955d 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -45,6 +45,10 @@ namespace MWClass std::string getModel(const MWWorld::ConstPtr &ptr) const override; + std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n + /// Second item in the pair specifies the error message + std::unique_ptr use (const MWWorld::Ptr& ptr, bool force=false) const override; ///< Generate action for using via inventory menu diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index f7cbc30ae1..f464eed88f 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -549,6 +549,19 @@ namespace MWGui } } + // Separate check for the soul gems. + else if (::Misc::StringUtils::ciEqualPrefix("misc_soulgem", ptr.getCellRef().getRefId())) + { + std::pair canEquip = ptr.getClass().canBeEquipped(ptr, player); + + if (canEquip.first == 0) + { + MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); + updateItemView(); + return; + } + } + // If the item has a script, set OnPCEquip or PCSkipEquip to 1 if (!script.empty()) { diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 742cfb1828..1fafbbd5e8 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -304,11 +304,38 @@ namespace MWScript MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.begin(); - for (; it != invStore.end(); ++it) + + // With soul gems we prefer filled ones. + + if (::Misc::StringUtils::ciEqualPrefix("misc_soulgem", item)) { - if (::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) - break; + it = invStore.end(); + + for (auto it_any = invStore.begin(); it_any != invStore.end(); ++it_any) + { + if (::Misc::StringUtils::ciEqual(it_any->getCellRef().getRefId(), item)) + { + if (!it_any->getCellRef().getSoul().empty() && + MWBase::Environment::get().getWorld()->getStore().get().search(it_any->getCellRef().getSoul())) + { + it = it_any; + break; + } + else if (it == invStore.end()) + it = it_any; + } + } } + + else + { + for (; it != invStore.end(); ++it) + { + if (::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) + break; + } + } + if (it == invStore.end()) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), item, 1); diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 3fa82a27fe..db1ed65719 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -97,6 +97,34 @@ public: return ciEqual(x, std::string_view(y, n - 1)); } + template + static bool ciEqualPrefix(const X& x, const Y& y) + { + return std::equal(std::begin(x), std::end(x), std::begin(y), + [] (char l, char r) { return toLower(l) == toLower(r); }); + } + + template + static auto ciEqualPrefix(const char(& x)[n], const char(& y)[n]) + { + static_assert(n > 0); + return ciEqualPrefix(std::string_view(x, n - 1), std::string_view(y, n - 1)); + } + + template + static auto ciEqualPrefix(const char(& x)[n], const T& y) + { + static_assert(n > 0); + return ciEqualPrefix(std::string_view(x, n - 1), y); + } + + template + static auto ciEqualPrefix(const T& x, const char(& y)[n]) + { + static_assert(n > 0); + return ciEqualPrefix(x, std::string_view(y, n - 1)); + } + static int ciCompareLen(std::string_view x, std::string_view y, std::size_t len) { std::string_view::const_iterator xit = x.begin(); From 3698d96b46338b1c72698b0ed288ac23a56a6aa9 Mon Sep 17 00:00:00 2001 From: myrix Date: Mon, 25 Jul 2022 01:47:10 +0300 Subject: [PATCH 2/4] use ciCompareLen, move checks to Miscellaneous::use and ActionSoulgem --- apps/openmw/mwclass/misc.cpp | 28 +++++--------------- apps/openmw/mwclass/misc.hpp | 4 --- apps/openmw/mwgui/inventorywindow.cpp | 13 --------- apps/openmw/mwscript/containerextensions.cpp | 4 ++- apps/openmw/mwworld/actionsoulgem.cpp | 25 ++++++++++++++++- components/misc/stringops.hpp | 28 -------------------- 6 files changed, 33 insertions(+), 69 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 53c3bc4bcb..4e165d6526 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -201,30 +201,14 @@ namespace MWClass return newPtr; } - std::pair Miscellaneous::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const - { - // Special checks for soul gems. - - if (::Misc::StringUtils::ciEqualPrefix("misc_soulgem", ptr.getCellRef().getRefId())) - { - if (npc != MWBase::Environment::get().getWorld()->getPlayerPtr()) - return std::make_pair(0, "Only the player can use soul gems."); - - if (ptr.getCellRef().getSoul().empty()) - return std::make_pair(0, "#{sNotifyMessage32}"); - - if (!MWBase::Environment::get().getWorld()->getStore().get().search(ptr.getCellRef().getSoul())) - return std::make_pair(0, "Unknown soul creature id."); - } - - return std::make_pair(1, ""); - } - std::unique_ptr Miscellaneous::use (const MWWorld::Ptr& ptr, bool force) const { - if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get().search(ptr.getCellRef().getSoul())) - return std::make_unique(); - return std::make_unique(ptr); + const char soulgem_prefix[] = "misc_soulgem"; + + if (::Misc::StringUtils::ciCompareLen(ptr.getCellRef().getRefId(), soulgem_prefix, sizeof(soulgem_prefix) - 1) == 0) + return std::make_unique(ptr); + + return std::make_unique(); } bool Miscellaneous::canSell (const MWWorld::ConstPtr& item, int npcServices) const diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 95d861955d..ebf38b10be 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -45,10 +45,6 @@ namespace MWClass std::string getModel(const MWWorld::ConstPtr &ptr) const override; - std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override; - ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n - /// Second item in the pair specifies the error message - std::unique_ptr use (const MWWorld::Ptr& ptr, bool force=false) const override; ///< Generate action for using via inventory menu diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index f464eed88f..f7cbc30ae1 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -549,19 +549,6 @@ namespace MWGui } } - // Separate check for the soul gems. - else if (::Misc::StringUtils::ciEqualPrefix("misc_soulgem", ptr.getCellRef().getRefId())) - { - std::pair canEquip = ptr.getClass().canBeEquipped(ptr, player); - - if (canEquip.first == 0) - { - MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); - updateItemView(); - return; - } - } - // If the item has a script, set OnPCEquip or PCSkipEquip to 1 if (!script.empty()) { diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 1fafbbd5e8..1bbaa9b60f 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -307,7 +307,9 @@ namespace MWScript // With soul gems we prefer filled ones. - if (::Misc::StringUtils::ciEqualPrefix("misc_soulgem", item)) + const char soulgem_prefix[] = "misc_soulgem"; + + if (::Misc::StringUtils::ciCompareLen(item, soulgem_prefix, sizeof(soulgem_prefix) - 1) == 0) { it = invStore.end(); diff --git a/apps/openmw/mwworld/actionsoulgem.cpp b/apps/openmw/mwworld/actionsoulgem.cpp index f7823dabaa..72efa6d4ec 100644 --- a/apps/openmw/mwworld/actionsoulgem.cpp +++ b/apps/openmw/mwworld/actionsoulgem.cpp @@ -1,10 +1,15 @@ #include "actionsoulgem.hpp" +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwmechanics/actorutil.hpp" +#include "../mwworld/esmstore.hpp" + namespace MWWorld { @@ -23,7 +28,25 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage5}"); return; } - MWBase::Environment::get().getWindowManager()->showSoulgemDialog(getTarget()); + + const auto& target = getTarget(); + const auto& targetSoul = target.getCellRef().getSoul(); + + if (targetSoul.empty()) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage32}"); + return; + } + + if (!MWBase::Environment::get().getWorld()->getStore().get().search(targetSoul)) + { + const auto& message = "Unknown soul creature id \"" + targetSoul + "\"."; + Log(Debug::Warning) << message; + MWBase::Environment::get().getWindowManager()->messageBox(message); + return; + } + + MWBase::Environment::get().getWindowManager()->showSoulgemDialog(target); } diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index db1ed65719..3fa82a27fe 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -97,34 +97,6 @@ public: return ciEqual(x, std::string_view(y, n - 1)); } - template - static bool ciEqualPrefix(const X& x, const Y& y) - { - return std::equal(std::begin(x), std::end(x), std::begin(y), - [] (char l, char r) { return toLower(l) == toLower(r); }); - } - - template - static auto ciEqualPrefix(const char(& x)[n], const char(& y)[n]) - { - static_assert(n > 0); - return ciEqualPrefix(std::string_view(x, n - 1), std::string_view(y, n - 1)); - } - - template - static auto ciEqualPrefix(const char(& x)[n], const T& y) - { - static_assert(n > 0); - return ciEqualPrefix(std::string_view(x, n - 1), y); - } - - template - static auto ciEqualPrefix(const T& x, const char(& y)[n]) - { - static_assert(n > 0); - return ciEqualPrefix(x, std::string_view(y, n - 1)); - } - static int ciCompareLen(std::string_view x, std::string_view y, std::size_t len) { std::string_view::const_iterator xit = x.begin(); From 782cb7699d9acc4939df70fb8ac1165cd02fa461 Mon Sep 17 00:00:00 2001 From: myrix Date: Mon, 25 Jul 2022 02:56:25 +0300 Subject: [PATCH 3/4] use std::string, no unlocalized message, show soul gem id --- apps/openmw/mwclass/misc.cpp | 4 ++-- apps/openmw/mwscript/containerextensions.cpp | 4 ++-- apps/openmw/mwworld/actionsoulgem.cpp | 6 ++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 4e165d6526..3aa5544b74 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -203,9 +203,9 @@ namespace MWClass std::unique_ptr Miscellaneous::use (const MWWorld::Ptr& ptr, bool force) const { - const char soulgem_prefix[] = "misc_soulgem"; + const std::string soulgemPrefix = "misc_soulgem"; - if (::Misc::StringUtils::ciCompareLen(ptr.getCellRef().getRefId(), soulgem_prefix, sizeof(soulgem_prefix) - 1) == 0) + if (::Misc::StringUtils::ciCompareLen(ptr.getCellRef().getRefId(), soulgemPrefix, soulgemPrefix.length()) == 0) return std::make_unique(ptr); return std::make_unique(); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 1bbaa9b60f..8699d3dfe4 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -307,9 +307,9 @@ namespace MWScript // With soul gems we prefer filled ones. - const char soulgem_prefix[] = "misc_soulgem"; + const std::string soulgemPrefix = "misc_soulgem"; - if (::Misc::StringUtils::ciCompareLen(item, soulgem_prefix, sizeof(soulgem_prefix) - 1) == 0) + if (::Misc::StringUtils::ciCompareLen(item, soulgemPrefix, soulgemPrefix.length()) == 0) { it = invStore.end(); diff --git a/apps/openmw/mwworld/actionsoulgem.cpp b/apps/openmw/mwworld/actionsoulgem.cpp index 72efa6d4ec..b416581fa7 100644 --- a/apps/openmw/mwworld/actionsoulgem.cpp +++ b/apps/openmw/mwworld/actionsoulgem.cpp @@ -30,7 +30,7 @@ namespace MWWorld } const auto& target = getTarget(); - const auto& targetSoul = target.getCellRef().getSoul(); + const std::string targetSoul = target.getCellRef().getSoul(); if (targetSoul.empty()) { @@ -40,9 +40,7 @@ namespace MWWorld if (!MWBase::Environment::get().getWorld()->getStore().get().search(targetSoul)) { - const auto& message = "Unknown soul creature id \"" + targetSoul + "\"."; - Log(Debug::Warning) << message; - MWBase::Environment::get().getWindowManager()->messageBox(message); + Log(Debug::Warning) << "Soul '" << targetSoul << "' not found (item: '" << target.getCellRef().getRefId() << "')"; return; } From dba0609f247834e011bab5d8c48ad22a0b361a40 Mon Sep 17 00:00:00 2001 From: myrix Date: Mon, 25 Jul 2022 05:15:13 +0300 Subject: [PATCH 4/4] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34ea5acc9d..da42274f49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,7 @@ Bug #6799: Game crashes if an NPC has no Class attached Bug #6849: ImageButton texture is not scaled properly Bug #6895: Removing a negative number of items from a script, makes the script terminate with an error + Bug #6901: Morrowind.exe soul gem usage discrepancy Feature #890: OpenMW-CS: Column filtering Feature #1465: "Reset" argument for AI functions Feature #2491: Ability to make OpenMW "portable"