From a662a00c628b33a9a55bc3e0a025f1568cf9ed4b Mon Sep 17 00:00:00 2001 From: MatthewRock Date: Tue, 10 Nov 2015 19:18:02 +0100 Subject: [PATCH 1/5] Fixing bug for merchant --- apps/openmw/mwworld/containerstore.cpp | 52 ++++++++++++++++++++------ apps/openmw/mwworld/containerstore.hpp | 7 ++-- components/esm/inventorystate.cpp | 9 ++++- components/esm/inventorystate.hpp | 2 +- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index bcaaeff94..78a50b4e7 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -442,9 +442,11 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: // For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks if (!levItem.empty() && count < 0) { - if (mLevelledItemMap.find(id) == mLevelledItemMap.end()) - mLevelledItemMap[id] = 0; - mLevelledItemMap[id] += std::abs(count); + //If there is no item in map, insert it + std::map >::iterator itemInMap = + mLevelledItemMap.insert(std::make_pair(id, std::make_pair(0, levItem))).first; + //Update spawned count + itemInMap->second.first += std::abs(count); } count = std::abs(count); @@ -461,30 +463,56 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner) { - // Remove the items already spawned by levelled items that will restock - for (std::map::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) + //allowedForReplace - Holds information about how many items from the list were sold; + // Hence, tells us how many items we need to restock. + //allowedForReplace[list] <- How many items we should generate(how many of these were sold) + std::map allowedForReplace; + + //Check which lists need restocking: + for (std::map >::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { - if (count(it->first) >= it->second) - remove(it->first, it->second, ptr); + int spawnedCount = it->second.first; //How many items should be in shop originally + int itemCount = count(it->first); //How many items are there in shop now + //If anything was sold + if(itemCount < spawnedCount) + { + //Create entry if it does not exist yet + std::map::iterator listInMap = allowedForReplace.insert( + std::make_pair(it->second.second, 0)).first; + //And signal that we need to restock item from this list + listInMap->second += std::abs(spawnedCount - itemCount); + //Also, remove the record if item no longer figures in the shop + if(!itemCount) + mLevelledItemMap.erase(it->first); + //If there's still item in the shop, change its spawnedCount to current count. + else + it->second.first -= itemCount; + } } - mLevelledItemMap.clear(); + //Restock: + //For every item that NPC could have for (std::vector::const_iterator it = items.mList.begin(); it != items.mList.end(); ++it) { + //If he shouldn't have it restocked, don't restock it. if (it->mCount >= 0) continue; - std::string item = Misc::StringUtils::lowerCase(it->mItem.toString()); + std::string itemOrList = Misc::StringUtils::lowerCase(it->mItem.toString()); + //If it's levelled list, restock if there's need to do so. if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mItem.toString())) { - addInitialItem(item, owner, it->mCount, true); + std::map::iterator listInMap = allowedForReplace.find(itemOrList); + if(listInMap != allowedForReplace.end()) + addInitialItem(itemOrList, owner, listInMap->second, true); } else { - int currentCount = count(item); + //Restocking static item - just restock to the max count + int currentCount = count(itemOrList); if (currentCount < std::abs(it->mCount)) - addInitialItem(item, owner, std::abs(it->mCount) - currentCount, true); + addInitialItem(itemOrList, owner, std::abs(it->mCount) - currentCount, true); } } flagAsModified(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index e9750a622..aaf83755a 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -68,9 +69,9 @@ namespace MWWorld MWWorld::CellRefList repairs; MWWorld::CellRefList weapons; - std::map mLevelledItemMap; - ///< Stores result of levelled item spawns. - /// This is used to remove the spawned item(s) if the levelled item is restocked. + std::map > mLevelledItemMap; + ///< Stores result of levelled item spawns. + /// This is used to restock levelled items(s) if the old item was sold. mutable float mCachedWeight; mutable bool mWeightUpToDate; diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index e7257ae53..1864b6e8d 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -36,6 +36,10 @@ void ESM::InventoryState::load (ESMReader &esm) { std::string id = esm.getHString(); int count; + std::string parentList; + //TODO: How should I handle old saves? + if(esm.isNextSub("LLST")) + std::string parentList = esm.getHString(); esm.getHNT (count, "COUN"); mLevelledItemMap[id] = count; } @@ -79,10 +83,11 @@ void ESM::InventoryState::save (ESMWriter &esm) const iter->save (esm, true); } - for (std::map::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) + for (std::map >::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { esm.writeHNString ("LEVM", it->first); - esm.writeHNT ("COUN", it->second); + esm.writeHNT ("COUN", it->second.first); + esm.writeHNString("LLST", it->second.second) } for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index d5c317beb..a12be321f 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -20,7 +20,7 @@ namespace ESM // std::map mEquipmentSlots; - std::map mLevelledItemMap; + std::map > mLevelledItemMap; typedef std::map > > TEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes; From aa721fe1f662e9e9781b9522c26f2f4dd2994303 Mon Sep 17 00:00:00 2001 From: MatthewRock Date: Tue, 8 Dec 2015 22:38:26 +0100 Subject: [PATCH 2/5] Fix bug 2952 with merchant and levelled items --- AUTHORS.md | 1 + apps/openmw/mwworld/containerstore.cpp | 55 ++++++++++++++++++++------ components/esm/inventorystate.cpp | 17 ++++---- 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index e968d1d01..077878ea2 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -74,6 +74,7 @@ Programmers Marco Melletti (mellotanica) Marco Schulze Mateusz Kołaczek (PL_kolek) + Mateusz Malisz (malice) megaton Michael Hogan (Xethik) Michael Mc Donnell diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 78a50b4e7..1130ec604 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -415,6 +415,7 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std:: void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel, const std::string& levItem) { + if (count == 0) return; //Don't restock with nothing. try { ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); @@ -463,8 +464,8 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner) { - //allowedForReplace - Holds information about how many items from the list were sold; - // Hence, tells us how many items we need to restock. + //allowedForReplace - Holds information about how many items from the list were not sold; + // Hence, tells us how many items we don't need to restock. //allowedForReplace[list] <- How many items we should generate(how many of these were sold) std::map allowedForReplace; @@ -473,20 +474,42 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW { int spawnedCount = it->second.first; //How many items should be in shop originally int itemCount = count(it->first); //How many items are there in shop now - //If anything was sold - if(itemCount < spawnedCount) + //If something was not sold + if(itemCount >= spawnedCount) + { + const std::string& ancestor = it->second.second; + // Security check for old saves: + //If item is imported from old save(doesn't have an ancestor) and wasn't sold + if(ancestor == "") + { + //Remove it, from shop, + remove(it->first, itemCount, ptr);//ptr is the NPC + //And remove it from map, so that when we restock, the new item will have proper ancestor. + mLevelledItemMap.erase(it); + continue; + + } + //Create the entry if it does not exist yet + std::map::iterator listInMap = allowedForReplace.insert( + std::make_pair(it->second.second, 0)).first; + //And signal that we don't need to restock item from this list + listInMap->second += std::abs(itemCount); + } + //If every of the item was sold + else if (itemCount == 0) + { + mLevelledItemMap.erase(it); + } + //If some was sold, but some remain + else { //Create entry if it does not exist yet std::map::iterator listInMap = allowedForReplace.insert( std::make_pair(it->second.second, 0)).first; - //And signal that we need to restock item from this list - listInMap->second += std::abs(spawnedCount - itemCount); - //Also, remove the record if item no longer figures in the shop - if(!itemCount) - mLevelledItemMap.erase(it->first); - //If there's still item in the shop, change its spawnedCount to current count. - else - it->second.first -= itemCount; + //And signal that we don't need to restock all items from this list + listInMap->second += std::abs(itemCount); + //And update itemCount so we don't mistake it next time. + it->second.first = itemCount; } } @@ -504,8 +527,14 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mItem.toString())) { std::map::iterator listInMap = allowedForReplace.find(itemOrList); + + int restockNum = it-> mCount; + //If we know we must restock less, take it into account if(listInMap != allowedForReplace.end()) - addInitialItem(itemOrList, owner, listInMap->second, true); + restockNum += listInMap->second;//We add, because list items have negative count + //restock + addInitialItem(itemOrList, owner, restockNum, true); + } else { diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index 1864b6e8d..ad9e70af1 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -31,17 +31,20 @@ void ESM::InventoryState::load (ESMReader &esm) ++index; } - + //Next item is Levelled item while (esm.isNextSub("LEVM")) { + //Get its name std::string id = esm.getHString(); int count; - std::string parentList; - //TODO: How should I handle old saves? - if(esm.isNextSub("LLST")) - std::string parentList = esm.getHString(); + std::string parentGroup = ""; + //Then get its count esm.getHNT (count, "COUN"); - mLevelledItemMap[id] = count; + //Old save formats don't have information about parent group; check for that + if(esm.isNextSub("LGRP")) + //Newest saves contain parent group + parentGroup = esm.getHString(); + mLevelledItemMap[id] = std::make_pair(count, parentGroup); } while (esm.isNextSub("MAGI")) @@ -87,7 +90,7 @@ void ESM::InventoryState::save (ESMWriter &esm) const { esm.writeHNString ("LEVM", it->first); esm.writeHNT ("COUN", it->second.first); - esm.writeHNString("LLST", it->second.second) + esm.writeHNString("LGRP", it->second.second); } for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) From b0e6a525956cbac9420624b619f4446528392c66 Mon Sep 17 00:00:00 2001 From: MatthewRock Date: Tue, 8 Dec 2015 22:45:16 +0100 Subject: [PATCH 3/5] Replace ancestor with parent --- apps/openmw/mwworld/containerstore.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 1130ec604..99e051ea7 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -477,14 +477,14 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW //If something was not sold if(itemCount >= spawnedCount) { - const std::string& ancestor = it->second.second; + const std::string& parent = it->second.second; // Security check for old saves: - //If item is imported from old save(doesn't have an ancestor) and wasn't sold - if(ancestor == "") + //If item is imported from old save(doesn't have an parent) and wasn't sold + if(parent == "") { //Remove it, from shop, remove(it->first, itemCount, ptr);//ptr is the NPC - //And remove it from map, so that when we restock, the new item will have proper ancestor. + //And remove it from map, so that when we restock, the new item will have proper parent. mLevelledItemMap.erase(it); continue; From 3b254ad6319a6687dcbf3b7e4f10679a7cceb259 Mon Sep 17 00:00:00 2001 From: MatthewRock Date: Wed, 9 Dec 2015 18:24:35 +0100 Subject: [PATCH 4/5] Allows the same item to have multiple ancestors --- apps/openmw/mwworld/containerstore.cpp | 34 +++++++++++++------------- apps/openmw/mwworld/containerstore.hpp | 4 +-- components/esm/inventorystate.cpp | 10 ++++---- components/esm/inventorystate.hpp | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 99e051ea7..5844cfa9a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -444,12 +444,12 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: if (!levItem.empty() && count < 0) { //If there is no item in map, insert it - std::map >::iterator itemInMap = - mLevelledItemMap.insert(std::make_pair(id, std::make_pair(0, levItem))).first; + std::map, int>::iterator itemInMap = + mLevelledItemMap.insert(std::make_pair(std::make_pair(id, levItem), 0)).first; //Update spawned count - itemInMap->second.first += std::abs(count); + itemInMap->second += std::abs(count); } - count = std::abs(count); + count = std::abs(count); ref.getPtr().getCellRef().setOwner(owner); addImp (ref.getPtr(), count); @@ -470,47 +470,48 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW std::map allowedForReplace; //Check which lists need restocking: - for (std::map >::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) + for (std::map, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end();) { - int spawnedCount = it->second.first; //How many items should be in shop originally - int itemCount = count(it->first); //How many items are there in shop now + int spawnedCount = it->second; //How many items should be in shop originally + int itemCount = count(it->first.first); //How many items are there in shop now //If something was not sold if(itemCount >= spawnedCount) { - const std::string& parent = it->second.second; + const std::string& parent = it->first.second; // Security check for old saves: //If item is imported from old save(doesn't have an parent) and wasn't sold if(parent == "") { //Remove it, from shop, - remove(it->first, itemCount, ptr);//ptr is the NPC + remove(it->first.first, itemCount, ptr);//ptr is the NPC //And remove it from map, so that when we restock, the new item will have proper parent. - mLevelledItemMap.erase(it); + mLevelledItemMap.erase(it++); continue; - } //Create the entry if it does not exist yet std::map::iterator listInMap = allowedForReplace.insert( - std::make_pair(it->second.second, 0)).first; + std::make_pair(it->first.second, 0)).first; //And signal that we don't need to restock item from this list listInMap->second += std::abs(itemCount); } //If every of the item was sold else if (itemCount == 0) { - mLevelledItemMap.erase(it); + mLevelledItemMap.erase(it++); + continue; } //If some was sold, but some remain else { //Create entry if it does not exist yet std::map::iterator listInMap = allowedForReplace.insert( - std::make_pair(it->second.second, 0)).first; + std::make_pair(it->first.second, 0)).first; //And signal that we don't need to restock all items from this list listInMap->second += std::abs(itemCount); //And update itemCount so we don't mistake it next time. - it->second.first = itemCount; + it->second = itemCount; } + ++it; } //Restock: @@ -528,13 +529,12 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW { std::map::iterator listInMap = allowedForReplace.find(itemOrList); - int restockNum = it-> mCount; + int restockNum = it->mCount; //If we know we must restock less, take it into account if(listInMap != allowedForReplace.end()) restockNum += listInMap->second;//We add, because list items have negative count //restock addInitialItem(itemOrList, owner, restockNum, true); - } else { diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index aaf83755a..876821f94 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -69,8 +69,8 @@ namespace MWWorld MWWorld::CellRefList repairs; MWWorld::CellRefList weapons; - std::map > mLevelledItemMap; - ///< Stores result of levelled item spawns. + std::map, int> mLevelledItemMap; + ///< Stores result of levelled item spawns. <(refId, spawningGroup), count> /// This is used to restock levelled items(s) if the old item was sold. mutable float mCachedWeight; diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index ad9e70af1..b24128ec3 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -44,7 +44,7 @@ void ESM::InventoryState::load (ESMReader &esm) if(esm.isNextSub("LGRP")) //Newest saves contain parent group parentGroup = esm.getHString(); - mLevelledItemMap[id] = std::make_pair(count, parentGroup); + mLevelledItemMap[std::make_pair(id, parentGroup)] = count; } while (esm.isNextSub("MAGI")) @@ -86,11 +86,11 @@ void ESM::InventoryState::save (ESMWriter &esm) const iter->save (esm, true); } - for (std::map >::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) + for (std::map, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { - esm.writeHNString ("LEVM", it->first); - esm.writeHNT ("COUN", it->second.first); - esm.writeHNString("LGRP", it->second.second); + esm.writeHNString ("LEVM", it->first.first); + esm.writeHNT ("COUN", it->second); + esm.writeHNString("LGRP", it->first.second); } for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index a12be321f..f4bb0ab48 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -20,7 +20,7 @@ namespace ESM // std::map mEquipmentSlots; - std::map > mLevelledItemMap; + std::map, int> mLevelledItemMap; typedef std::map > > TEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes; From ddd4004c959252871aa5b0e8487322c2c25406ff Mon Sep 17 00:00:00 2001 From: MatthewRock Date: Wed, 9 Dec 2015 18:26:33 +0100 Subject: [PATCH 5/5] Fix: remove space --- apps/openmw/mwworld/containerstore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 5844cfa9a..b45e7ef83 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -449,7 +449,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: //Update spawned count itemInMap->second += std::abs(count); } - count = std::abs(count); + count = std::abs(count); ref.getPtr().getCellRef().setOwner(owner); addImp (ref.getPtr(), count);