diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d2a67349b..174eb9e4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,7 +80,9 @@ Bug #6386: Artifacts in water reflection due to imprecise screen-space coordinate computation Bug #6396: Inputting certain Unicode characters triggers an assertion Bug #6416: Morphs are applied to the wrong target + Bug #6417: OpenMW doesn't always use the right node to accumulate movement Bug #6429: Wyrmhaven: Can't add AI packages to player + Bug #6433: Items bound to Quick Keys sometimes do not appear until the Quick Key menu is opened. Feature #890: OpenMW-CS: Column filtering Feature #1465: "Reset" argument for AI functions Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 961a63ac79..e7d22bef92 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -69,6 +69,7 @@ namespace MWGui class DialogueWindow; class WindowModal; class JailScreen; + class MessageBox; enum ShowInDialogueMode { ShowInDialogueMode_IfPossible, @@ -145,6 +146,7 @@ namespace MWBase virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0; + virtual const std::vector getActiveMessageBoxes() = 0; /// Make the player use an item, while updating GUI state accordingly virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index f77b274f30..1eee2bb857 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -170,6 +170,12 @@ namespace MWGui return false; } + const std::vector MessageBoxManager::getActiveMessageBoxes() + { + return mMessageBoxes; + } + + int MessageBoxManager::readPressedButton (bool reset) { int pressed = mLastButtonPressed; @@ -211,6 +217,11 @@ namespace MWGui mMainWidget->setPosition(pos); } + const std::string MessageBox::getMessage() + { + return mMessage; + } + int MessageBox::getHeight () { return mMainWidget->getHeight()+mNextBoxPadding; diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index b7c8e91b99..6a4cda948e 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -49,6 +49,8 @@ namespace MWGui void setVisible(bool value); + const std::vector getActiveMessageBoxes(); + private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; @@ -63,6 +65,7 @@ namespace MWGui public: MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message); void setMessage (const std::string& message); + const std::string getMessage(); int getHeight (); void update (int height); void setVisible(bool value); @@ -73,7 +76,7 @@ namespace MWGui protected: MessageBoxManager& mMessageBoxManager; - const std::string& mMessage; + const std::string mMessage; MyGUI::EditBox* mMessageWidget; int mBottomPadding; int mNextBoxPadding; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index ad61f63b90..e55b9b4878 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -79,41 +79,45 @@ namespace MWGui delete mMagicSelectionDialog; } + inline void QuickKeysMenu::validate(int index) + { + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); + switch (mKey[index].type) + { + case Type_Unassigned: + case Type_HandToHand: + case Type_Magic: + break; + case Type_Item: + case Type_MagicItem: + { + MWWorld::Ptr item = *mKey[index].button->getUserData(); + // Make sure the item is available and is not broken + if (!item || item.getRefData().getCount() < 1 || + (item.getClass().hasItemHealth(item) && + item.getClass().getItemHealth(item) <= 0)) + { + // Try searching for a compatible replacement + item = store.findReplacement(mKey[index].id); + + if (item) + mKey[index].button->setUserData(MWWorld::Ptr(item)); + + break; + } + } + } + } + void QuickKeysMenu::onOpen() { WindowBase::onOpen(); - MWWorld::Ptr player = MWMechanics::getPlayer(); - MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); - - // Check if quick keys are still valid - for (int i=0; i<10; ++i) + // Quick key index + for (int index = 0; index < 10; ++index) { - switch (mKey[i].type) - { - case Type_Unassigned: - case Type_HandToHand: - case Type_Magic: - break; - case Type_Item: - case Type_MagicItem: - { - MWWorld::Ptr item = *mKey[i].button->getUserData(); - // Make sure the item is available and is not broken - if (!item || item.getRefData().getCount() < 1 || - (item.getClass().hasItemHealth(item) && - item.getClass().getItemHealth(item) <= 0)) - { - // Try searching for a compatible replacement - item = store.findReplacement(mKey[i].id); - - if (item) - mKey[i].button->setUserData(MWWorld::Ptr(item)); - - break; - } - } - } + validate(index); } } @@ -329,11 +333,13 @@ namespace MWGui assert(index >= 1 && index <= 10); keyData *key = &mKey[index-1]; - + MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player); + validate(index-1); + // Delay action executing, // if player is busy for now (casting a spell, attacking someone, etc.) bool isDelayNeeded = MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player) diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index b2742df796..4761c98ceb 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -76,7 +76,8 @@ namespace MWGui void onQuickKeyButtonClicked(MyGUI::Widget* sender); void onOkButtonClicked(MyGUI::Widget* sender); - + // Check if quick key is still valid + inline void validate(int index); void unassign(keyData* key); }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a935d7f900..2eff1f6c84 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -771,6 +771,11 @@ namespace MWGui mMessageBoxManager->removeStaticMessageBox(); } + const std::vector WindowManager::getActiveMessageBoxes() + { + return mMessageBoxManager->getActiveMessageBoxes(); + } + int WindowManager::readPressedButton () { return mMessageBoxManager->readPressedButton(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 9ec79e0c82..10577c8485 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -187,6 +187,7 @@ namespace MWGui MWGui::CountDialog* getCountDialog() override; MWGui::ConfirmationDialog* getConfirmationDialog() override; MWGui::TradeWindow* getTradeWindow() override; + const std::vector getActiveMessageBoxes() override; /// Make the player use an item, while updating GUI state accordingly void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions=false) override; diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp index 3931517fab..84bdc7d12b 100644 --- a/apps/openmw/mwinput/actionmanager.cpp +++ b/apps/openmw/mwinput/actionmanager.cpp @@ -22,6 +22,8 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" +#include "../mwgui/messagebox.hpp" + #include "actions.hpp" #include "bindingsmanager.hpp" @@ -40,6 +42,7 @@ namespace MWInput , mSneaking(false) , mAttemptJump(false) , mTimeIdle(0.f) + , mOverencumberedMessage("#{sNotifyMessage59}") { } @@ -96,7 +99,17 @@ namespace MWInput if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr)) { player.setAutoMove (false); - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage59}"); + std::vector msgboxs = MWBase::Environment::get().getWindowManager()->getActiveMessageBoxes(); + const std::vector::iterator it = std::find_if(msgboxs.begin(), msgboxs.end(), [=](MWGui::MessageBox*& msgbox) + { + return (msgbox->getMessage() == mOverencumberedMessage); + }); + + // if an overencumbered messagebox is already present, reset its expiry timer, otherwise create new one. + if (it != msgboxs.end()) + (*it)->mCurrentTime = 0; + else + MWBase::Environment::get().getWindowManager()->messageBox(mOverencumberedMessage); } } diff --git a/apps/openmw/mwinput/actionmanager.hpp b/apps/openmw/mwinput/actionmanager.hpp index 4c51139d46..b3deaed20e 100644 --- a/apps/openmw/mwinput/actionmanager.hpp +++ b/apps/openmw/mwinput/actionmanager.hpp @@ -67,6 +67,8 @@ namespace MWInput bool mSneaking; bool mAttemptJump; + const std::string mOverencumberedMessage; + float mTimeIdle; }; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ecfe65c575..bf0ed04055 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -624,9 +624,8 @@ namespace MWRender return; const NodeMap& nodeMap = getNodeMap(); - - for (SceneUtil::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin(); - it != animsrc->mKeyframes->mKeyframeControllers.end(); ++it) + const auto& controllerMap = animsrc->mKeyframes->mKeyframeControllers; + for (SceneUtil::KeyframeHolder::KeyframeControllerMap::const_iterator it = controllerMap.begin(); it != controllerMap.end(); ++it) { std::string bonename = Misc::StringUtils::lowerCase(it->first); NodeMap::const_iterator found = nodeMap.find(bonename); @@ -652,14 +651,32 @@ namespace MWRender SceneUtil::AssignControllerSourcesVisitor assignVisitor(mAnimationTimePtr[0]); mObjectRoot->accept(assignVisitor); + // Determine the movement accumulation bone if necessary if (!mAccumRoot) { - NodeMap::const_iterator found = nodeMap.find("bip01"); - if (found == nodeMap.end()) - found = nodeMap.find("root bone"); - - if (found != nodeMap.end()) - mAccumRoot = found->second; + // Priority matters! bip01 is preferred. + static const std::array accumRootNames = + { + "bip01", + "root bone" + }; + NodeMap::const_iterator found = nodeMap.end(); + for (const std::string& name : accumRootNames) + { + found = nodeMap.find(name); + if (found == nodeMap.end()) + continue; + for (SceneUtil::KeyframeHolder::KeyframeControllerMap::const_iterator it = controllerMap.begin(); it != controllerMap.end(); ++it) + { + if (Misc::StringUtils::lowerCase(it->first) == name) + { + mAccumRoot = found->second; + break; + } + } + if (mAccumRoot) + break; + } } } diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 86571e1e3a..c828e1ca81 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -56,8 +56,6 @@ static const char exec_err[] = "!!! Failed to exec debug process\n"; static char argv0[PATH_MAX]; -static char altstack[SIGSTKSZ]; - static struct { int signum; @@ -475,9 +473,10 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig /* Set an alternate signal stack so SIGSEGVs caused by stack overflows * still run */ + static char* altstack = new char [SIGSTKSZ]; altss.ss_sp = altstack; altss.ss_flags = 0; - altss.ss_size = sizeof(altstack); + altss.ss_size = SIGSTKSZ; sigaltstack(&altss, nullptr); memset(&sa, 0, sizeof(sa));