From b0f98687e6a9c2d9e240fd47c8a7b5bd64c5572c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Oct 2014 17:45:18 +0200 Subject: [PATCH 1/6] Properly handle DialInfo records that were marked as Deleted (Fixes #2035) --- apps/openmw/mwworld/esmstore.cpp | 1 + apps/openmw/mwworld/store.hpp | 19 +++++++++++++++++++ components/esm/loaddial.cpp | 11 +++++++++++ components/esm/loaddial.hpp | 3 +++ 4 files changed, 34 insertions(+) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 1b5d3d1e9..56cb05c64 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -135,6 +135,7 @@ void ESMStore::setUp() mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); + mDialogs.setUp(); } int ESMStore::countSavedGameRecords() const diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 107db68b1..2611bacbd 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -1174,6 +1174,25 @@ namespace MWWorld mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); } + template<> + inline void Store::setUp() + { + // DialInfos marked as deleted are kept during the loading phase, so that the linked list + // structure is kept intact for inserting further INFOs. Delete them now that loading is done. + for (Static::iterator it = mStatic.begin(); it != mStatic.end(); ++it) + { + ESM::Dialogue& dial = it->second; + dial.clearDeletedInfos(); + } + + mShared.clear(); + mShared.reserve(mStatic.size()); + typename std::map::iterator it = mStatic.begin(); + for (; it != mStatic.end(); ++it) { + mShared.push_back(&(it->second)); + } + } + } //end namespace #endif diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index 92077b572..ff0362aa2 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -110,4 +110,15 @@ void Dialogue::readInfo(ESMReader &esm, bool merge) std::cerr << "Failed to insert info " << id << std::endl; } +void Dialogue::clearDeletedInfos() +{ + for (InfoContainer::iterator it = mInfo.begin(); it != mInfo.end(); ) + { + if (it->mQuestStatus == DialInfo::QS_Deleted) + it = mInfo.erase(it); + else + ++it; + } +} + } diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index fd46ad210..d29948c63 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -47,6 +47,9 @@ struct Dialogue void load(ESMReader &esm); void save(ESMWriter &esm) const; + /// Remove all INFOs marked as QS_Deleted from mInfos. + void clearDeletedInfos(); + /// Read the next info record /// @param merge Merge with existing list, or just push each record to the end of the list? void readInfo (ESM::ESMReader& esm, bool merge); From 7f06e3e7e308f99c14e83aee82286528154d7bab Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Oct 2014 17:07:54 +0200 Subject: [PATCH 2/6] Fix alchemy producing potion IDs from content files --- apps/openmw/mwmechanics/alchemy.cpp | 61 +++++++++++++++-------------- apps/openmw/mwmechanics/alchemy.hpp | 12 ++---- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 90c180817..5549c85ed 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -205,7 +205,7 @@ void MWMechanics::Alchemy::updateEffects() } } -const ESM::Potion *MWMechanics::Alchemy::getRecord() const +const ESM::Potion *MWMechanics::Alchemy::getRecord(const ESM::Potion& toFind) const { const MWWorld::Store &potions = MWBase::Environment::get().getWorld()->getStore().get(); @@ -216,6 +216,18 @@ const ESM::Potion *MWMechanics::Alchemy::getRecord() const if (iter->mEffects.mList.size() != mEffects.size()) continue; + if (iter->mName != toFind.mName + || iter->mScript != toFind.mScript + || iter->mData.mWeight != toFind.mData.mWeight + || iter->mData.mValue != toFind.mData.mValue + || iter->mData.mAutoCalc != toFind.mData.mAutoCalc) + continue; + + // Don't choose an ID that came from the content files, would have unintended side effects + // where alchemy can be used to produce quest-relevant items + if (!potions.isDynamic(iter->mId)) + continue; + bool mismatch = false; for (int i=0; i (iter->mEffects.mList.size()); ++i) @@ -266,37 +278,34 @@ void MWMechanics::Alchemy::removeIngredients() void MWMechanics::Alchemy::addPotion (const std::string& name) { - const ESM::Potion *record = getRecord(); + ESM::Potion newRecord; - if (!record) - { - ESM::Potion newRecord; + newRecord.mData.mWeight = 0; - newRecord.mData.mWeight = 0; - - for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) - if (!iter->isEmpty()) - newRecord.mData.mWeight += iter->get()->mBase->mData.mWeight; + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) + if (!iter->isEmpty()) + newRecord.mData.mWeight += iter->get()->mBase->mData.mWeight; - newRecord.mData.mWeight /= countIngredients(); + newRecord.mData.mWeight /= countIngredients(); - newRecord.mData.mValue = mValue; - newRecord.mData.mAutoCalc = 0; + newRecord.mData.mValue = mValue; + newRecord.mData.mAutoCalc = 0; - newRecord.mName = name; + newRecord.mName = name; - int index = static_cast (std::rand()/(static_cast (RAND_MAX)+1)*6); - assert (index>=0 && index<6); + int index = static_cast (std::rand()/(static_cast (RAND_MAX)+1)*6); + assert (index>=0 && index<6); - static const char *name[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" }; + static const char *meshes[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" }; - newRecord.mModel = "m\\misc_potion_" + std::string (name[index]) + "_01.nif"; - newRecord.mIcon = "m\\tx_potion_" + std::string (name[index]) + "_01.dds"; + newRecord.mModel = "m\\misc_potion_" + std::string (meshes[index]) + "_01.nif"; + newRecord.mIcon = "m\\tx_potion_" + std::string (meshes[index]) + "_01.dds"; - newRecord.mEffects.mList = mEffects; + newRecord.mEffects.mList = mEffects; + const ESM::Potion* record = getRecord(newRecord); + if (!record) record = MWBase::Environment::get().getWorld()->createRecord (newRecord); - } mAlchemist.getClass().getContainerStore (mAlchemist).add (record->mId, 1, mAlchemist); } @@ -436,14 +445,6 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const return mEffects.end(); } -std::string MWMechanics::Alchemy::getPotionName() const -{ - if (const ESM::Potion *potion = getRecord()) - return potion->mName; - - return ""; -} - MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) { if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) @@ -452,7 +453,7 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na if (countIngredients()<2) return Result_LessThanTwoIngredients; - if (name.empty() && getPotionName().empty()) + if (name.empty()) return Result_NoName; if (listEffects().empty()) diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index e6b8c6650..c96356ebb 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -56,8 +56,9 @@ namespace MWMechanics void updateEffects(); - const ESM::Potion *getRecord() const; - ///< Return existing record for created potion (may return 0) + const ESM::Potion *getRecord(const ESM::Potion& toFind) const; + ///< Try to find a potion record similar to \a toFind in the record store, or return 0 if not found + /// \note Does not account for record ID, model or icon void removeIngredients(); ///< Remove selected ingredients from alchemist's inventory, cleanup selected ingredients and @@ -108,15 +109,10 @@ namespace MWMechanics void removeIngredient (int index); ///< Remove ingredient from slot (calling this function on an empty slot is a no-op). - std::string getPotionName() const; - ///< Return the name of the potion that would be created when calling create (if a record for such - /// a potion already exists) or return an empty string. - Result create (const std::string& name); ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. - /// \param name must not be an empty string, unless there is already a potion record ( - /// getPotionName() does not return an empty string). + /// \param name must not be an empty string, or Result_NoName is returned }; } From 29ac97be7adf4d8bb0156dceadb378f6d87be389 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Oct 2014 17:28:22 +0200 Subject: [PATCH 3/6] Add automatic potion naming --- apps/openmw/mwgui/alchemywindow.cpp | 5 +++++ apps/openmw/mwgui/alchemywindow.hpp | 2 ++ apps/openmw/mwmechanics/alchemy.cpp | 11 +++++++++++ apps/openmw/mwmechanics/alchemy.hpp | 3 +++ 4 files changed, 21 insertions(+) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 0fa9121b7..e67a8512a 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -173,6 +173,11 @@ namespace MWGui void AlchemyWindow::update() { + std::string suggestedName = mAlchemy.suggestPotionName(); + if (suggestedName != mSuggestedPotionName) + mNameEdit->setCaptionWithReplacing(suggestedName); + mSuggestedPotionName = suggestedName; + mSortModel->clearDragItems(); MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy.beginIngredients (); diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index b538a1f80..36f540c1b 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -23,6 +23,8 @@ namespace MWGui virtual void exit(); private: + std::string mSuggestedPotionName; + ItemView* mItemView; SortFilterItemModel* mSortModel; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 5549c85ed..328f3a25d 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -480,3 +480,14 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na return Result_Success; } + +std::string MWMechanics::Alchemy::suggestPotionName() +{ + std::set effects = listEffects(); + if (effects.empty()) + return ""; + + int effectId = effects.begin()->mId; + return MWBase::Environment::get().getWorld()->getStore().get().find( + ESM::MagicEffect::effectIdToString(effectId)); +} diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index c96356ebb..caba26f14 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -109,6 +109,9 @@ namespace MWMechanics void removeIngredient (int index); ///< Remove ingredient from slot (calling this function on an empty slot is a no-op). + std::string suggestPotionName (); + ///< Suggest a name for the potion, based on the current effects + Result create (const std::string& name); ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. From ace8e0175bdc63d2354c969fddf7730d31797506 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Oct 2014 17:27:25 +0200 Subject: [PATCH 4/6] Fix old alchemy apparatus still showing in alchemy window after removal --- apps/openmw/mwgui/alchemywindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index e67a8512a..b9e0044ce 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -134,11 +134,12 @@ namespace MWGui for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy.beginTools()); iter!=mAlchemy.endTools() && index (mApparatus.size()); ++iter, ++index) { + mApparatus.at (index)->setItem(*iter); + mApparatus.at (index)->clearUserStrings(); if (!iter->isEmpty()) { mApparatus.at (index)->setUserString ("ToolTipType", "ItemPtr"); mApparatus.at (index)->setUserData (*iter); - mApparatus.at (index)->setItem(*iter); } } From 9bb51fd9c2f65d13548e115011d91cc993e10035 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Oct 2014 17:42:51 +0200 Subject: [PATCH 5/6] Compile fix --- apps/openmw/mwmechanics/alchemy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 328f3a25d..da2492a77 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -489,5 +489,5 @@ std::string MWMechanics::Alchemy::suggestPotionName() int effectId = effects.begin()->mId; return MWBase::Environment::get().getWorld()->getStore().get().find( - ESM::MagicEffect::effectIdToString(effectId)); + ESM::MagicEffect::effectIdToString(effectId))->getString(); } From b5a57920b640c4251d302b0400cde2e3e37e49ae Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Tue, 21 Oct 2014 19:35:17 +0200 Subject: [PATCH 6/6] Fix compile error on Windows --- apps/openmw/mwworld/store.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 2611bacbd..55c5b8191 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -1187,7 +1187,7 @@ namespace MWWorld mShared.clear(); mShared.reserve(mStatic.size()); - typename std::map::iterator it = mStatic.begin(); + std::map::iterator it = mStatic.begin(); for (; it != mStatic.end(); ++it) { mShared.push_back(&(it->second)); }