mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 02:45:32 +00:00
Rework container resolution (#3006)
* Rework container resolution * add optional argument to getCount * remove now-redundant changes * undo worldimp changes * move save-fixing code to InventoryState * replace Rng instances with Seeds
This commit is contained in:
parent
c99be77a32
commit
72549651e0
30 changed files with 371 additions and 308 deletions
|
@ -5,8 +5,11 @@
|
|||
Bug #1952: Incorrect particle lighting
|
||||
Bug #2069: Fireflies in Fireflies invade Morrowind look wrong
|
||||
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
||||
Bug #2473: Unable to overstock merchants
|
||||
Bug #3676: NiParticleColorModifier isn't applied properly
|
||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||
Bug #3862: Random container contents behave differently than vanilla
|
||||
Bug #3929: Leveled list merchant containers respawn on barter
|
||||
Bug #4021: Attributes and skills are not stored as floats
|
||||
Bug #4055: Local scripts don't inherit variables from their base record
|
||||
Bug #4623: Corprus implementation is incorrect
|
||||
|
|
|
@ -31,44 +31,41 @@
|
|||
|
||||
namespace MWClass
|
||||
{
|
||||
class ContainerCustomData : public MWWorld::CustomData
|
||||
ContainerCustomData::ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell)
|
||||
{
|
||||
public:
|
||||
MWWorld::ContainerStore mContainerStore;
|
||||
unsigned int seed = Misc::Rng::rollDice(std::numeric_limits<int>::max());
|
||||
// setting ownership not needed, since taking items from a container inherits the
|
||||
// container's owner automatically
|
||||
mStore.fillNonRandom(container.mInventory, "", seed);
|
||||
}
|
||||
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
|
||||
virtual ContainerCustomData& asContainerCustomData()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual const ContainerCustomData& asContainerCustomData() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
ContainerCustomData::ContainerCustomData(const ESM::InventoryState& inventory)
|
||||
{
|
||||
mStore.readState(inventory);
|
||||
}
|
||||
|
||||
MWWorld::CustomData *ContainerCustomData::clone() const
|
||||
{
|
||||
return new ContainerCustomData (*this);
|
||||
}
|
||||
|
||||
ContainerCustomData& ContainerCustomData::asContainerCustomData()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
const ContainerCustomData& ContainerCustomData::asContainerCustomData() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::unique_ptr<ContainerCustomData> data (new ContainerCustomData);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Container> *ref =
|
||||
ptr.get<ESM::Container>();
|
||||
|
||||
// setting ownership not needed, since taking items from a container inherits the
|
||||
// container's owner automatically
|
||||
data->mContainerStore.fill(
|
||||
ref->mBase->mInventory, "");
|
||||
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
|
||||
|
||||
// store
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
ptr.getRefData().setCustomData (std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()).release());
|
||||
|
||||
MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());
|
||||
}
|
||||
|
@ -98,17 +95,6 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
void Container::restock(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
|
||||
const ESM::InventoryList& list = ref->mBase->mInventory;
|
||||
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
||||
|
||||
// setting ownership not needed, since taking items from a container inherits the
|
||||
// container's owner automatically
|
||||
store.restock(list, ptr, "");
|
||||
}
|
||||
|
||||
void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
||||
{
|
||||
if (!model.empty()) {
|
||||
|
@ -228,12 +214,12 @@ namespace MWClass
|
|||
return !name.empty() ? name : ref->mBase->mId;
|
||||
}
|
||||
|
||||
MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr)
|
||||
const
|
||||
MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return ptr.getRefData().getCustomData()->asContainerCustomData().mContainerStore;
|
||||
auto& data = ptr.getRefData().getCustomData()->asContainerCustomData();
|
||||
data.mStore.mPtr = ptr;
|
||||
return data.mStore;
|
||||
}
|
||||
|
||||
std::string Container::getScript (const MWWorld::ConstPtr& ptr) const
|
||||
|
@ -253,8 +239,7 @@ namespace MWClass
|
|||
bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
|
||||
return !canBeHarvested(ptr) || data->asContainerCustomData().mContainerStore.hasVisibleItems();
|
||||
|
||||
return !canBeHarvested(ptr) || data->asContainerCustomData().mStore.hasVisibleItems();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -317,28 +302,20 @@ namespace MWClass
|
|||
if (!state.mHasCustomState)
|
||||
return;
|
||||
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||
std::unique_ptr<ContainerCustomData> data (new ContainerCustomData);
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
}
|
||||
|
||||
ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
|
||||
const ESM::ContainerState& containerState = state.asContainerState();
|
||||
customData.mContainerStore.readState (containerState.mInventory);
|
||||
ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory).release());
|
||||
}
|
||||
|
||||
void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
|
||||
if (!ptr.getRefData().getCustomData() || !customData.mStore.isResolved())
|
||||
{
|
||||
state.mHasCustomState = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
|
||||
ESM::ContainerState& containerState = state.asContainerState();
|
||||
customData.mContainerStore.writeState (containerState.mInventory);
|
||||
customData.mStore.writeState (containerState.mInventory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,32 @@
|
|||
#define GAME_MWCLASS_CONTAINER_H
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/customdata.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Container;
|
||||
struct InventoryState;
|
||||
}
|
||||
|
||||
namespace MWClass
|
||||
{
|
||||
class ContainerCustomData : public MWWorld::CustomData
|
||||
{
|
||||
MWWorld::ContainerStore mStore;
|
||||
public:
|
||||
ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell);
|
||||
ContainerCustomData(const ESM::InventoryState& inventory);
|
||||
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
|
||||
virtual ContainerCustomData& asContainerCustomData();
|
||||
virtual const ContainerCustomData& asContainerCustomData() const;
|
||||
|
||||
friend class Container;
|
||||
};
|
||||
|
||||
class Container : public MWWorld::Class
|
||||
{
|
||||
void ensureCustomData (const MWWorld::Ptr& ptr) const;
|
||||
|
@ -60,8 +83,6 @@ namespace MWClass
|
|||
|
||||
virtual void respawn (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual void restock (const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
||||
virtual bool useAnim() const;
|
||||
|
|
|
@ -842,14 +842,6 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
void Creature::restock(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
const ESM::InventoryList& list = ref->mBase->mInventory;
|
||||
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
||||
store.restock(list, ptr, ptr.getCellRef().getRefId());
|
||||
}
|
||||
|
||||
int Creature::getBaseFightRating(const MWWorld::ConstPtr &ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
|
|
|
@ -123,8 +123,6 @@ namespace MWClass
|
|||
|
||||
virtual void respawn (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual void restock (const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual int getBaseFightRating(const MWWorld::ConstPtr &ptr) const;
|
||||
|
||||
virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const;
|
||||
|
|
|
@ -1401,14 +1401,6 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
void Npc::restock(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
||||
const ESM::InventoryList& list = ref->mBase->mInventory;
|
||||
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
||||
store.restock(list, ptr, ptr.getCellRef().getRefId());
|
||||
}
|
||||
|
||||
int Npc::getBaseFightRating (const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
||||
|
|
|
@ -158,8 +158,6 @@ namespace MWClass
|
|||
|
||||
virtual void respawn (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual void restock (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const;
|
||||
|
||||
virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -168,6 +168,7 @@ namespace MWGui
|
|||
|
||||
if (!mPtr.isEmpty())
|
||||
MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);
|
||||
resetReference();
|
||||
}
|
||||
|
||||
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
||||
|
|
|
@ -39,22 +39,29 @@ namespace
|
|||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems)
|
||||
: mItemSources(itemSources)
|
||||
, mWorldItems(worldItems)
|
||||
: mWorldItems(worldItems)
|
||||
, mTrading(true)
|
||||
{
|
||||
assert (!mItemSources.empty());
|
||||
assert (!itemSources.empty());
|
||||
// Tie resolution lifetimes to the ItemModel
|
||||
mItemSources.reserve(itemSources.size());
|
||||
for(const MWWorld::Ptr& source : itemSources)
|
||||
{
|
||||
MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
|
||||
mItemSources.push_back(std::make_pair(source, store.resolveTemporarily()));
|
||||
}
|
||||
}
|
||||
|
||||
ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)
|
||||
ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) : mTrading(false)
|
||||
{
|
||||
mItemSources.push_back(source);
|
||||
MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
|
||||
mItemSources.push_back(std::make_pair(source, store.resolveTemporarily()));
|
||||
}
|
||||
|
||||
bool ContainerItemModel::allowedToUseItems() const
|
||||
{
|
||||
if (mItemSources.size() == 0)
|
||||
if (mItemSources.empty())
|
||||
return true;
|
||||
|
||||
MWWorld::Ptr ptr = MWMechanics::getPlayer();
|
||||
|
@ -62,7 +69,7 @@ bool ContainerItemModel::allowedToUseItems() const
|
|||
|
||||
// Check if the player is allowed to use items from opened container
|
||||
MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();
|
||||
return mm->isAllowedToUse(ptr, mItemSources[0], victim);
|
||||
return mm->isAllowedToUse(ptr, mItemSources[0].first, victim);
|
||||
}
|
||||
|
||||
ItemStack ContainerItemModel::getItem (ModelIndex index)
|
||||
|
@ -93,25 +100,31 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
|
|||
|
||||
MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
|
||||
{
|
||||
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
|
||||
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
|
||||
auto& source = mItemSources[0];
|
||||
MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);
|
||||
if (item.mBase.getContainerStore() == &store)
|
||||
throw std::runtime_error("Item to copy needs to be from a different container!");
|
||||
return *source.getClass().getContainerStore(source).add(item.mBase, count, source, allowAutoEquip);
|
||||
return *store.add(item.mBase, count, source.first, allowAutoEquip);
|
||||
}
|
||||
|
||||
void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
|
||||
{
|
||||
int toRemove = count;
|
||||
|
||||
for (MWWorld::Ptr& source : mItemSources)
|
||||
for (auto& source : mItemSources)
|
||||
{
|
||||
MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
|
||||
MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
if (stacks(*it, item.mBase))
|
||||
{
|
||||
toRemove -= store.remove(*it, toRemove, source);
|
||||
int quantity = it->mRef->mData.getCount(false);
|
||||
// If this is a restocking quantity, just don't remove it
|
||||
if(quantity < 0 && mTrading)
|
||||
toRemove += quantity;
|
||||
else
|
||||
toRemove -= store.remove(*it, toRemove, source.first);
|
||||
if (toRemove <= 0)
|
||||
return;
|
||||
}
|
||||
|
@ -138,9 +151,9 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
|
|||
void ContainerItemModel::update()
|
||||
{
|
||||
mItems.clear();
|
||||
for (MWWorld::Ptr& source : mItemSources)
|
||||
for (auto& source : mItemSources)
|
||||
{
|
||||
MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
|
||||
MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
|
@ -194,7 +207,7 @@ bool ContainerItemModel::onDropItem(const MWWorld::Ptr &item, int count)
|
|||
if (mItemSources.empty())
|
||||
return false;
|
||||
|
||||
MWWorld::Ptr target = mItemSources[0];
|
||||
MWWorld::Ptr target = mItemSources[0].first;
|
||||
|
||||
if (target.getTypeName() != typeid(ESM::Container).name())
|
||||
return true;
|
||||
|
@ -224,7 +237,7 @@ bool ContainerItemModel::onTakeItem(const MWWorld::Ptr &item, int count)
|
|||
if (mItemSources.empty())
|
||||
return false;
|
||||
|
||||
MWWorld::Ptr target = mItemSources[0];
|
||||
MWWorld::Ptr target = mItemSources[0].first;
|
||||
|
||||
// Looting a dead corpse is considered OK
|
||||
if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
#ifndef MWGUI_CONTAINER_ITEM_MODEL_H
|
||||
#define MWGUI_CONTAINER_ITEM_MODEL_H
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "itemmodel.hpp"
|
||||
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
|
@ -32,9 +37,9 @@ namespace MWGui
|
|||
virtual void update();
|
||||
|
||||
private:
|
||||
std::vector<MWWorld::Ptr> mItemSources;
|
||||
std::vector<std::pair<MWWorld::Ptr, MWWorld::ResolutionHandle>> mItemSources;
|
||||
std::vector<MWWorld::Ptr> mWorldItems;
|
||||
|
||||
const bool mTrading;
|
||||
std::vector<ItemStack> mItems;
|
||||
};
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace MWGui
|
|||
mBorrowedToUs.clear();
|
||||
}
|
||||
|
||||
std::vector<ItemStack> TradeItemModel::getItemsBorrowedToUs()
|
||||
const std::vector<ItemStack> TradeItemModel::getItemsBorrowedToUs() const
|
||||
{
|
||||
return mBorrowedToUs;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace MWGui
|
|||
/// and removing weight for items we've lent to someone else.
|
||||
void adjustEncumbrance (float& encumbrance);
|
||||
|
||||
std::vector<ItemStack> getItemsBorrowedToUs();
|
||||
const std::vector<ItemStack> getItemsBorrowedToUs() const;
|
||||
|
||||
private:
|
||||
void borrowImpl(const ItemStack& item, std::vector<ItemStack>& out);
|
||||
|
|
|
@ -99,20 +99,6 @@ namespace MWGui
|
|||
setCoord(400, 0, 400, 300);
|
||||
}
|
||||
|
||||
void TradeWindow::restock()
|
||||
{
|
||||
// Restock items on the actor inventory
|
||||
mPtr.getClass().restock(mPtr);
|
||||
|
||||
// Also restock any containers owned by this merchant, which are also available to buy in the trade window
|
||||
std::vector<MWWorld::Ptr> itemSources;
|
||||
MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources);
|
||||
for (MWWorld::Ptr& source : itemSources)
|
||||
{
|
||||
source.getClass().restock(source);
|
||||
}
|
||||
}
|
||||
|
||||
void TradeWindow::setPtr(const MWWorld::Ptr& actor)
|
||||
{
|
||||
mPtr = actor;
|
||||
|
@ -121,10 +107,10 @@ namespace MWGui
|
|||
mCurrentMerchantOffer = 0;
|
||||
|
||||
std::vector<MWWorld::Ptr> itemSources;
|
||||
// Important: actor goes first, so purchased items come out of the actor's pocket first
|
||||
itemSources.push_back(actor);
|
||||
MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources);
|
||||
|
||||
// Important: actor goes last, so that items purchased by the merchant go into his inventory
|
||||
itemSources.push_back(actor);
|
||||
std::vector<MWWorld::Ptr> worldItems;
|
||||
MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems);
|
||||
|
||||
|
@ -281,8 +267,8 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
// were there any items traded at all?
|
||||
std::vector<ItemStack> playerBought = playerItemModel->getItemsBorrowedToUs();
|
||||
std::vector<ItemStack> merchantBought = mTradeModel->getItemsBorrowedToUs();
|
||||
const std::vector<ItemStack>& playerBought = playerItemModel->getItemsBorrowedToUs();
|
||||
const std::vector<ItemStack>& merchantBought = mTradeModel->getItemsBorrowedToUs();
|
||||
if (playerBought.empty() && merchantBought.empty())
|
||||
{
|
||||
// user notification
|
||||
|
@ -313,7 +299,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
// check if the player is attempting to sell back an item stolen from this actor
|
||||
for (ItemStack& itemStack : merchantBought)
|
||||
for (const ItemStack& itemStack : merchantBought)
|
||||
{
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(itemStack.mBase.getCellRef().getRefId(), mPtr))
|
||||
{
|
||||
|
@ -363,8 +349,6 @@ namespace MWGui
|
|||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Item Gold Up");
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
|
||||
|
||||
restock();
|
||||
}
|
||||
|
||||
void TradeWindow::onAccept(MyGUI::EditBox *sender)
|
||||
|
@ -478,7 +462,7 @@ namespace MWGui
|
|||
// connected to buying and selling the same item.
|
||||
// This value has been determined by researching the limitations of the vanilla formula
|
||||
// and may not be sufficient if getBarterOffer behavior has been changed.
|
||||
std::vector<ItemStack> playerBorrowed = playerTradeModel->getItemsBorrowedToUs();
|
||||
const std::vector<ItemStack>& playerBorrowed = playerTradeModel->getItemsBorrowedToUs();
|
||||
for (const ItemStack& itemStack : playerBorrowed)
|
||||
{
|
||||
const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
|
||||
|
@ -487,7 +471,7 @@ namespace MWGui
|
|||
merchantOffer -= std::max(cap, buyingPrice);
|
||||
}
|
||||
|
||||
std::vector<ItemStack> merchantBorrowed = mTradeModel->getItemsBorrowedToUs();
|
||||
const std::vector<ItemStack>& merchantBorrowed = mTradeModel->getItemsBorrowedToUs();
|
||||
for (const ItemStack& itemStack : merchantBorrowed)
|
||||
{
|
||||
const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
|
||||
|
@ -532,4 +516,9 @@ namespace MWGui
|
|||
mTradeModel = nullptr;
|
||||
mSortModel = nullptr;
|
||||
}
|
||||
|
||||
void TradeWindow::onClose()
|
||||
{
|
||||
resetReference();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace MWGui
|
|||
|
||||
void setPtr(const MWWorld::Ptr& actor);
|
||||
|
||||
virtual void onClose() override;
|
||||
void onFrame(float dt);
|
||||
void clear() { resetReference(); }
|
||||
|
||||
|
@ -111,8 +112,6 @@ namespace MWGui
|
|||
virtual void onReferenceUnavailable();
|
||||
|
||||
int getMerchantGold();
|
||||
|
||||
void restock();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,14 @@ namespace MWMechanics
|
|||
{
|
||||
|
||||
/// @return ID of resulting item, or empty if none
|
||||
inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature)
|
||||
inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Seed& seed = Misc::Rng::getSeed())
|
||||
{
|
||||
const std::vector<ESM::LevelledListBase::LevelItem>& items = levItem->mList;
|
||||
|
||||
const MWWorld::Ptr& player = getPlayer();
|
||||
int playerLevel = player.getClass().getCreatureStats(player).getLevel();
|
||||
|
||||
if (Misc::Rng::roll0to99() < levItem->mChanceNone)
|
||||
if (Misc::Rng::roll0to99(seed) < levItem->mChanceNone)
|
||||
return std::string();
|
||||
|
||||
std::vector<std::string> candidates;
|
||||
|
@ -55,7 +55,7 @@ namespace MWMechanics
|
|||
}
|
||||
if (candidates.empty())
|
||||
return std::string();
|
||||
std::string item = candidates[Misc::Rng::rollDice(candidates.size())];
|
||||
std::string item = candidates[Misc::Rng::rollDice(candidates.size(), seed)];
|
||||
|
||||
// Vanilla doesn't fail on nonexistent items in levelled lists
|
||||
if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item)))
|
||||
|
@ -74,9 +74,9 @@ namespace MWMechanics
|
|||
else
|
||||
{
|
||||
if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name())
|
||||
return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
|
||||
return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false, seed);
|
||||
else
|
||||
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true);
|
||||
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true, seed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1045,6 +1045,7 @@ namespace MWMechanics
|
|||
void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer)
|
||||
{
|
||||
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
|
||||
MWWorld::ContainerStore& containerStore = targetContainer.getClass().getContainerStore(targetContainer);
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getCellRef().getRefId()));
|
||||
|
@ -1065,7 +1066,7 @@ namespace MWMechanics
|
|||
|
||||
int toMove = it->getRefData().getCount() - itemCount;
|
||||
|
||||
targetContainer.getClass().getContainerStore(targetContainer).add(*it, toMove, targetContainer);
|
||||
containerStore.add(*it, toMove, targetContainer);
|
||||
store.remove(*it, toMove, player);
|
||||
}
|
||||
// TODO: unhardcode the locklevel
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace MWWorld
|
|||
|
||||
MWWorld::Ptr target = getTarget();
|
||||
MWWorld::ContainerStore& store = target.getClass().getContainerStore (target);
|
||||
store.resolve();
|
||||
MWWorld::ContainerStore& actorStore = actor.getClass().getContainerStore(actor);
|
||||
std::map<std::string, int> takenMap;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
|
|
|
@ -1030,7 +1030,8 @@ namespace MWWorld
|
|||
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0)
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0
|
||||
&& ptr.getClass().getContainerStore(ptr).isResolved())
|
||||
{
|
||||
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||
}
|
||||
|
|
|
@ -353,8 +353,6 @@ namespace MWWorld
|
|||
|
||||
virtual void respawn (const MWWorld::Ptr& ptr) const {}
|
||||
|
||||
virtual void restock (const MWWorld::Ptr& ptr) const {}
|
||||
|
||||
/// Returns sound id
|
||||
virtual std::string getSound(const MWWorld::ConstPtr& ptr) const;
|
||||
|
||||
|
|
|
@ -23,6 +23,21 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
void addScripts(MWWorld::ContainerStore& store, MWWorld::CellStore* cell)
|
||||
{
|
||||
auto& scripts = MWBase::Environment::get().getWorld()->getLocalScripts();
|
||||
for(const MWWorld::Ptr& ptr : store)
|
||||
{
|
||||
const std::string& script = ptr.getClass().getScript(ptr);
|
||||
if(!script.empty())
|
||||
{
|
||||
MWWorld::Ptr item = ptr;
|
||||
item.mCell = cell;
|
||||
scripts.add(script, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
float getTotalWeight (const MWWorld::CellRefList<T>& cellRefList)
|
||||
{
|
||||
|
@ -44,6 +59,7 @@ namespace
|
|||
MWWorld::Ptr searchId (MWWorld::CellRefList<T>& list, const std::string& id,
|
||||
MWWorld::ContainerStore *store)
|
||||
{
|
||||
store->resolve();
|
||||
std::string id2 = Misc::StringUtils::lowerCase (id);
|
||||
|
||||
for (typename MWWorld::CellRefList<T>::List::iterator iter (list.mList.begin());
|
||||
|
@ -61,6 +77,18 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
MWWorld::ResolutionListener::~ResolutionListener()
|
||||
{
|
||||
if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
|
||||
{
|
||||
for(const MWWorld::Ptr& ptr : mStore)
|
||||
ptr.getRefData().setCount(0);
|
||||
mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
|
||||
addScripts(mStore, mStore.mPtr.mCell);
|
||||
mStore.mResolved = false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList<T>& collection,
|
||||
const ESM::ObjectState& state)
|
||||
|
@ -119,7 +147,11 @@ MWWorld::ContainerStore::ContainerStore()
|
|||
: mListener(nullptr)
|
||||
, mRechargingItemsUpToDate(false)
|
||||
, mCachedWeight (0)
|
||||
, mWeightUpToDate (false) {}
|
||||
, mWeightUpToDate (false)
|
||||
, mModified(false)
|
||||
, mResolved(false)
|
||||
, mSeed()
|
||||
, mPtr() {}
|
||||
|
||||
MWWorld::ContainerStore::~ContainerStore() {}
|
||||
|
||||
|
@ -153,22 +185,12 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()
|
|||
return ContainerStoreIterator (this);
|
||||
}
|
||||
|
||||
int MWWorld::ContainerStore::count(const std::string &id)
|
||||
int MWWorld::ContainerStore::count(const std::string &id) const
|
||||
{
|
||||
int total=0;
|
||||
for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
|
||||
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
|
||||
total += iter->getRefData().getCount();
|
||||
return total;
|
||||
}
|
||||
|
||||
int MWWorld::ContainerStore::restockCount(const std::string &id)
|
||||
{
|
||||
int total=0;
|
||||
for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
|
||||
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
|
||||
if (iter->getCellRef().getSoul().empty())
|
||||
total += iter->getRefData().getCount();
|
||||
for (const auto& iter : *this)
|
||||
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
|
||||
total += iter.getRefData().getCount();
|
||||
return total;
|
||||
}
|
||||
|
||||
|
@ -185,9 +207,10 @@ void MWWorld::ContainerStore::setContListener(MWWorld::ContainerStoreListener* l
|
|||
|
||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container, int count)
|
||||
{
|
||||
resolve();
|
||||
if (ptr.getRefData().getCount() <= count)
|
||||
return end();
|
||||
MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-count);
|
||||
MWWorld::ContainerStoreIterator it = addNewStack(ptr, subtractItems(ptr.getRefData().getCount(false), count));
|
||||
const std::string script = it->getClass().getScript(*it);
|
||||
if (!script.empty())
|
||||
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it);
|
||||
|
@ -199,6 +222,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr,
|
|||
|
||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item)
|
||||
{
|
||||
resolve();
|
||||
MWWorld::ContainerStoreIterator retval = end();
|
||||
for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)
|
||||
{
|
||||
|
@ -216,7 +240,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::
|
|||
{
|
||||
if (stacks(*iter, item))
|
||||
{
|
||||
iter->getRefData().setCount(iter->getRefData().getCount() + item.getRefData().getCount());
|
||||
iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), item.getRefData().getCount(false)));
|
||||
item.getRefData().setCount(0);
|
||||
retval = iter;
|
||||
break;
|
||||
|
@ -328,8 +352,10 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
|
|||
return it;
|
||||
}
|
||||
|
||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count)
|
||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count, bool markModified)
|
||||
{
|
||||
if(markModified)
|
||||
resolve();
|
||||
int type = getType(ptr);
|
||||
|
||||
const MWWorld::ESMStore &esmStore =
|
||||
|
@ -345,7 +371,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr,
|
|||
{
|
||||
if (Misc::StringUtils::ciEqual((*iter).getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))
|
||||
{
|
||||
iter->getRefData().setCount(iter->getRefData().getCount() + realCount);
|
||||
iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), realCount));
|
||||
flagAsModified();
|
||||
return iter;
|
||||
}
|
||||
|
@ -361,7 +387,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr,
|
|||
if (stacks(*iter, ptr))
|
||||
{
|
||||
// stack
|
||||
iter->getRefData().setCount( iter->getRefData().getCount() + count );
|
||||
iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));
|
||||
|
||||
flagAsModified();
|
||||
return iter;
|
||||
|
@ -439,6 +465,7 @@ void MWWorld::ContainerStore::updateRechargingItems()
|
|||
|
||||
int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
|
||||
{
|
||||
resolve();
|
||||
int toRemove = count;
|
||||
|
||||
for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)
|
||||
|
@ -465,6 +492,7 @@ bool MWWorld::ContainerStore::hasVisibleItems() const
|
|||
int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor)
|
||||
{
|
||||
assert(this == item.getContainerStore());
|
||||
resolve();
|
||||
|
||||
int toRemove = count;
|
||||
RefData& itemRef = item.getRefData();
|
||||
|
@ -476,7 +504,7 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
|
|||
}
|
||||
else
|
||||
{
|
||||
itemRef.setCount(itemRef.getCount() - toRemove);
|
||||
itemRef.setCount(subtractItems(itemRef.getCount(false), toRemove));
|
||||
toRemove = 0;
|
||||
}
|
||||
|
||||
|
@ -490,20 +518,33 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
|
|||
return count - toRemove;
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner)
|
||||
void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Seed& seed)
|
||||
{
|
||||
for (std::vector<ESM::ContItem>::const_iterator iter (items.mList.begin()); iter!=items.mList.end();
|
||||
++iter)
|
||||
for (const ESM::ContItem& iter : items.mList)
|
||||
{
|
||||
std::string id = Misc::StringUtils::lowerCase(iter->mItem);
|
||||
addInitialItem(id, owner, iter->mCount);
|
||||
std::string id = Misc::StringUtils::lowerCase(iter.mItem);
|
||||
addInitialItem(id, owner, iter.mCount, &seed);
|
||||
}
|
||||
|
||||
flagAsModified();
|
||||
mResolved = true;
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner,
|
||||
int count, bool topLevel, const std::string& levItem)
|
||||
void MWWorld::ContainerStore::fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed)
|
||||
{
|
||||
mSeed = seed;
|
||||
for (const ESM::ContItem& iter : items.mList)
|
||||
{
|
||||
std::string id = Misc::StringUtils::lowerCase(iter.mItem);
|
||||
addInitialItem(id, owner, iter.mCount, nullptr);
|
||||
}
|
||||
|
||||
flagAsModified();
|
||||
mResolved = false;
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count,
|
||||
Misc::Rng::Seed* seed, bool topLevel, const std::string& levItem)
|
||||
{
|
||||
if (count == 0) return; //Don't restock with nothing.
|
||||
try
|
||||
|
@ -511,13 +552,13 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
|
|||
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
|
||||
if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())
|
||||
{
|
||||
addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem);
|
||||
addInitialItemImp(ref.getPtr(), owner, count, seed, topLevel, levItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding just one item per time to make sure there isn't a stack of scripted items
|
||||
for (int i = 0; i < abs(count); i++)
|
||||
addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem);
|
||||
for (int i = 0; i < std::abs(count); i++)
|
||||
addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, seed, topLevel, levItem);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
@ -526,137 +567,43 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
|
|||
}
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner,
|
||||
int count, bool topLevel, const std::string& levItem)
|
||||
void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner, int count,
|
||||
Misc::Rng::Seed* seed, bool topLevel, const std::string& levItem)
|
||||
{
|
||||
if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())
|
||||
{
|
||||
if(!seed)
|
||||
return;
|
||||
const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;
|
||||
|
||||
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
|
||||
{
|
||||
for (int i=0; i<std::abs(count); ++i)
|
||||
addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, true, levItemList->mId);
|
||||
addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, seed, true, levItemList->mId);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false);
|
||||
std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false, *seed);
|
||||
if (itemId.empty())
|
||||
return;
|
||||
addInitialItem(itemId, owner, count, false, levItemList->mId);
|
||||
addInitialItem(itemId, owner, count, seed, false, levItemList->mId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A negative count indicates restocking items
|
||||
// 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 there is no item in map, insert it
|
||||
std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
|
||||
mLevelledItemMap.insert(std::make_pair(std::make_pair(ptr.getCellRef().getRefId(), levItem), 0)).first;
|
||||
//Update spawned count
|
||||
itemInMap->second += std::abs(count);
|
||||
}
|
||||
count = std::abs(count);
|
||||
|
||||
ptr.getCellRef().setOwner(owner);
|
||||
addImp (ptr, count);
|
||||
addImp (ptr, count, false);
|
||||
}
|
||||
}
|
||||
|
||||
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 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<std::string, int> allowedForReplace;
|
||||
|
||||
//Check which lists need restocking:
|
||||
for (std::map<std::pair<std::string, std::string>, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end();)
|
||||
{
|
||||
int spawnedCount = it->second; //How many items should be in shop originally
|
||||
int itemCount = restockCount(it->first.first); //How many items are there in shop now
|
||||
//If something was not sold
|
||||
if(itemCount >= spawnedCount)
|
||||
{
|
||||
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.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++);
|
||||
continue;
|
||||
}
|
||||
//Create the entry if it does not exist yet
|
||||
std::map<std::string, int>::iterator listInMap = allowedForReplace.insert(
|
||||
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++);
|
||||
continue;
|
||||
}
|
||||
//If some was sold, but some remain
|
||||
else
|
||||
{
|
||||
//Create entry if it does not exist yet
|
||||
std::map<std::string, int>::iterator listInMap = allowedForReplace.insert(
|
||||
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 = itemCount;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
//Restock:
|
||||
//For every item that NPC could have
|
||||
for (std::vector<ESM::ContItem>::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 itemOrList = Misc::StringUtils::lowerCase(it->mItem);
|
||||
|
||||
//If it's levelled list, restock if there's need to do so.
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().search(it->mItem))
|
||||
{
|
||||
std::map<std::string, int>::iterator listInMap = allowedForReplace.find(itemOrList);
|
||||
|
||||
int restockNum = std::abs(it->mCount);
|
||||
//If we know we must restock less, take it into account
|
||||
if(listInMap != allowedForReplace.end())
|
||||
restockNum -= std::min(restockNum, listInMap->second);
|
||||
//restock
|
||||
addInitialItem(itemOrList, owner, -restockNum, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Restocking static item - just restock to the max count
|
||||
int currentCount = restockCount(itemOrList);
|
||||
if (currentCount < std::abs(it->mCount))
|
||||
addInitialItem(itemOrList, owner, -(std::abs(it->mCount) - currentCount), true);
|
||||
}
|
||||
}
|
||||
flagAsModified();
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::clear()
|
||||
{
|
||||
for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
|
||||
iter->getRefData().setCount (0);
|
||||
|
||||
flagAsModified();
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::flagAsModified()
|
||||
|
@ -665,6 +612,45 @@ void MWWorld::ContainerStore::flagAsModified()
|
|||
mRechargingItemsUpToDate = false;
|
||||
}
|
||||
|
||||
bool MWWorld::ContainerStore::isResolved() const
|
||||
{
|
||||
return mResolved;
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::resolve()
|
||||
{
|
||||
if(!mResolved && !mPtr.isEmpty())
|
||||
{
|
||||
for(const MWWorld::Ptr& ptr : *this)
|
||||
ptr.getRefData().setCount(0);
|
||||
Misc::Rng::Seed seed{mSeed};
|
||||
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
|
||||
addScripts(*this, mPtr.mCell);
|
||||
}
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
MWWorld::ResolutionHandle MWWorld::ContainerStore::resolveTemporarily()
|
||||
{
|
||||
if(mModified)
|
||||
return {};
|
||||
std::shared_ptr<ResolutionListener> listener = mResolutionListener.lock();
|
||||
if(!listener)
|
||||
{
|
||||
listener = std::make_shared<ResolutionListener>(*this);
|
||||
mResolutionListener = listener;
|
||||
}
|
||||
if(!mResolved && !mPtr.isEmpty())
|
||||
{
|
||||
for(const MWWorld::Ptr& ptr : *this)
|
||||
ptr.getRefData().setCount(0);
|
||||
Misc::Rng::Seed seed{mSeed};
|
||||
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
|
||||
addScripts(*this, mPtr.mCell);
|
||||
}
|
||||
return {listener};
|
||||
}
|
||||
|
||||
float MWWorld::ContainerStore::getWeight() const
|
||||
{
|
||||
if (!mWeightUpToDate)
|
||||
|
@ -761,6 +747,7 @@ MWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)
|
|||
|
||||
MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
|
||||
{
|
||||
resolve();
|
||||
{
|
||||
Ptr ptr = searchId (potions, id, this);
|
||||
if (!ptr.isEmpty())
|
||||
|
@ -836,6 +823,22 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
|
|||
return Ptr();
|
||||
}
|
||||
|
||||
int MWWorld::ContainerStore::addItems(int count1, int count2)
|
||||
{
|
||||
int sum = std::abs(count1) + std::abs(count2);
|
||||
if(count1 < 0 || count2 < 0)
|
||||
return -sum;
|
||||
return sum;
|
||||
}
|
||||
|
||||
int MWWorld::ContainerStore::subtractItems(int count1, int count2)
|
||||
{
|
||||
int sum = std::abs(count1) - std::abs(count2);
|
||||
if(count1 < 0 || count2 < 0)
|
||||
return -sum;
|
||||
return sum;
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
|
||||
{
|
||||
state.mItems.clear();
|
||||
|
@ -853,13 +856,13 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
|
|||
storeStates (repairs, state, index);
|
||||
storeStates (weapons, state, index, true);
|
||||
storeStates (lights, state, index, true);
|
||||
|
||||
state.mLevelledItemMap = mLevelledItemMap;
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
|
||||
{
|
||||
clear();
|
||||
mModified = true;
|
||||
mResolved = true;
|
||||
|
||||
int index = 0;
|
||||
for (std::vector<ESM::ObjectState>::const_iterator
|
||||
|
@ -893,9 +896,6 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mLevelledItemMap = inventory.mLevelledItemMap;
|
||||
}
|
||||
|
||||
template<class PtrType>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <components/esm/loadalch.hpp>
|
||||
|
@ -18,6 +19,8 @@
|
|||
#include <components/esm/loadrepa.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "cellreflist.hpp"
|
||||
|
||||
|
@ -27,6 +30,11 @@ namespace ESM
|
|||
struct InventoryState;
|
||||
}
|
||||
|
||||
namespace MWClass
|
||||
{
|
||||
class Container;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ContainerStore;
|
||||
|
@ -37,6 +45,21 @@ namespace MWWorld
|
|||
typedef ContainerStoreIteratorBase<Ptr> ContainerStoreIterator;
|
||||
typedef ContainerStoreIteratorBase<ConstPtr> ConstContainerStoreIterator;
|
||||
|
||||
class ResolutionListener
|
||||
{
|
||||
ContainerStore& mStore;
|
||||
public:
|
||||
ResolutionListener(ContainerStore& store) : mStore(store) {}
|
||||
~ResolutionListener();
|
||||
};
|
||||
|
||||
class ResolutionHandle
|
||||
{
|
||||
std::shared_ptr<ResolutionListener> mListener;
|
||||
public:
|
||||
ResolutionHandle(std::shared_ptr<ResolutionListener> listener) : mListener(listener) {}
|
||||
ResolutionHandle() {}
|
||||
};
|
||||
|
||||
class ContainerStoreListener
|
||||
{
|
||||
|
@ -93,15 +116,18 @@ namespace MWWorld
|
|||
MWWorld::CellRefList<ESM::Repair> repairs;
|
||||
MWWorld::CellRefList<ESM::Weapon> weapons;
|
||||
|
||||
std::map<std::pair<std::string, std::string>, 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;
|
||||
mutable bool mWeightUpToDate;
|
||||
ContainerStoreIterator addImp (const Ptr& ptr, int count);
|
||||
void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
|
||||
void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
|
||||
|
||||
bool mModified;
|
||||
bool mResolved;
|
||||
unsigned int mSeed;
|
||||
MWWorld::Ptr mPtr;
|
||||
std::weak_ptr<ResolutionListener> mResolutionListener;
|
||||
|
||||
ContainerStoreIterator addImp (const Ptr& ptr, int count, bool markModified = true);
|
||||
void addInitialItem (const std::string& id, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true, const std::string& levItem = "");
|
||||
void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true, const std::string& levItem = "");
|
||||
|
||||
template<typename T>
|
||||
ContainerStoreIterator getState (CellRefList<T>& collection,
|
||||
|
@ -175,31 +201,31 @@ namespace MWWorld
|
|||
/// If a compatible stack is found, the item's count is added to that stack, then the original is deleted.
|
||||
/// @return If the item was stacked, return the stack, otherwise return the old (untouched) item.
|
||||
|
||||
int count (const std::string& id);
|
||||
int count (const std::string& id) const;
|
||||
///< @return How many items with refID \a id are in this container?
|
||||
|
||||
int restockCount (const std::string& id);
|
||||
///< Item count with restock adjustments (such as ignoring filled soul gems).
|
||||
/// @return How many items with refID \a id are in this container?
|
||||
|
||||
ContainerStoreListener* getContListener() const;
|
||||
void setContListener(ContainerStoreListener* listener);
|
||||
|
||||
protected:
|
||||
ContainerStoreIterator addNewStack (const ConstPtr& ptr, int count);
|
||||
///< Add the item to this container (do not try to stack it onto existing items)
|
||||
|
||||
virtual void flagAsModified();
|
||||
|
||||
/// + and - operations that can deal with negative stacks
|
||||
/// Note that negativity is infectious
|
||||
static int addItems(int count1, int count2);
|
||||
static int subtractItems(int count1, int count2);
|
||||
public:
|
||||
|
||||
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
||||
///< @return true if the two specified objects can stack with each other
|
||||
|
||||
void fill (const ESM::InventoryList& items, const std::string& owner);
|
||||
void fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Seed& seed = Misc::Rng::getSeed());
|
||||
///< Insert items into *this.
|
||||
|
||||
void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner);
|
||||
void fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed);
|
||||
///< Insert items into *this, excluding leveled items
|
||||
|
||||
virtual void clear();
|
||||
///< Empty container.
|
||||
|
@ -220,8 +246,15 @@ namespace MWWorld
|
|||
|
||||
virtual void readState (const ESM::InventoryState& state);
|
||||
|
||||
bool isResolved() const;
|
||||
|
||||
void resolve();
|
||||
ResolutionHandle resolveTemporarily();
|
||||
|
||||
friend class ContainerStoreIteratorBase<Ptr>;
|
||||
friend class ContainerStoreIteratorBase<ConstPtr>;
|
||||
friend class ResolutionListener;
|
||||
friend class MWClass::Container;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -86,8 +86,9 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt
|
|||
// unstack if required
|
||||
if (!allowedSlots.second && iter->getRefData().getCount() > 1)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1);
|
||||
iter->getRefData().setCount(iter->getRefData().getCount()-1);
|
||||
int count = iter->getRefData().getCount(false);
|
||||
MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, count > 0 ? 1 : -1);
|
||||
iter->getRefData().setCount(subtractItems(count, 1));
|
||||
mSlots[slot] = newIter;
|
||||
}
|
||||
else
|
||||
|
@ -850,8 +851,8 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(con
|
|||
{
|
||||
if (stacks(*iter, item) && !isEquipped(*iter))
|
||||
{
|
||||
iter->getRefData().setCount(iter->getRefData().getCount() + count);
|
||||
item.getRefData().setCount(item.getRefData().getCount() - count);
|
||||
iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));
|
||||
item.getRefData().setCount(subtractItems(item.getRefData().getCount(false), count));
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace
|
|||
// Ignore containers without generated content
|
||||
if (containerPtr.getTypeName() == typeid(ESM::Container).name() &&
|
||||
containerPtr.getRefData().getCustomData() == nullptr)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr);
|
||||
for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
|
||||
|
|
|
@ -146,8 +146,10 @@ namespace MWWorld
|
|||
return mBaseNode;
|
||||
}
|
||||
|
||||
int RefData::getCount() const
|
||||
int RefData::getCount(bool absolute) const
|
||||
{
|
||||
if(absolute)
|
||||
return std::abs(mCount);
|
||||
return mCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ namespace MWWorld
|
|||
/// Set base node (can be a null pointer).
|
||||
void setBaseNode (SceneUtil::PositionAttitudeTransform* base);
|
||||
|
||||
int getCount() const;
|
||||
int getCount(bool absolute = true) const;
|
||||
|
||||
void setLocals (const ESM::Script& script);
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace DetourNavigator
|
|||
dtPolyRef resultRef = 0;
|
||||
osg::Vec3f resultPosition;
|
||||
navMeshQuery.findRandomPointAroundCircle(startRef, start.ptr(), maxRadius, &queryFilter,
|
||||
&Misc::Rng::rollProbability, &resultRef, resultPosition.ptr());
|
||||
[]() { return Misc::Rng::rollProbability(); }, &resultRef, resultPosition.ptr());
|
||||
|
||||
if (resultRef == 0)
|
||||
return boost::optional<osg::Vec3f>();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
void ESM::InventoryState::load (ESMReader &esm)
|
||||
{
|
||||
// obsolete
|
||||
|
@ -106,6 +108,19 @@ void ESM::InventoryState::load (ESMReader &esm)
|
|||
|
||||
mSelectedEnchantItem = -1;
|
||||
esm.getHNOT(mSelectedEnchantItem, "SELE");
|
||||
|
||||
// Old saves had restocking levelled items in a special map
|
||||
// This turns items from that map into negative quantities
|
||||
for(const auto& entry : mLevelledItemMap)
|
||||
{
|
||||
const std::string& id = entry.first.first;
|
||||
const int count = entry.second;
|
||||
for(auto& item : mItems)
|
||||
{
|
||||
if(item.mCount == count && Misc::StringUtils::ciEqual(id, item.mRef.mRefID))
|
||||
item.mCount = -count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::InventoryState::save (ESMWriter &esm) const
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "esmwriter.hpp"
|
||||
|
||||
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
||||
int ESM::SavedGame::sCurrentFormat = 14;
|
||||
int ESM::SavedGame::sCurrentFormat = 15;
|
||||
|
||||
void ESM::SavedGame::load (ESMReader &esm)
|
||||
{
|
||||
|
|
|
@ -3,29 +3,44 @@
|
|||
#include <chrono>
|
||||
#include <random>
|
||||
|
||||
namespace
|
||||
{
|
||||
Misc::Rng::Seed sSeed;
|
||||
}
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
|
||||
std::mt19937 Rng::generator = std::mt19937();
|
||||
Rng::Seed::Seed() {}
|
||||
|
||||
Rng::Seed::Seed(unsigned int seed)
|
||||
{
|
||||
mGenerator.seed(seed);
|
||||
}
|
||||
|
||||
Rng::Seed& Rng::getSeed()
|
||||
{
|
||||
return sSeed;
|
||||
}
|
||||
|
||||
void Rng::init(unsigned int seed)
|
||||
{
|
||||
generator.seed(seed);
|
||||
sSeed.mGenerator.seed(seed);
|
||||
}
|
||||
|
||||
float Rng::rollProbability()
|
||||
float Rng::rollProbability(Seed& seed)
|
||||
{
|
||||
return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(generator);
|
||||
return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(sSeed.mGenerator);
|
||||
}
|
||||
|
||||
float Rng::rollClosedProbability()
|
||||
float Rng::rollClosedProbability(Seed& seed)
|
||||
{
|
||||
return std::uniform_real_distribution<float>(0, 1)(generator);
|
||||
return std::uniform_real_distribution<float>(0, 1)(sSeed.mGenerator);
|
||||
}
|
||||
|
||||
int Rng::rollDice(int max)
|
||||
int Rng::rollDice(int max, Seed& seed)
|
||||
{
|
||||
return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(generator) : 0;
|
||||
return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(sSeed.mGenerator) : 0;
|
||||
}
|
||||
|
||||
unsigned int Rng::generateDefaultSeed()
|
||||
|
|
|
@ -13,24 +13,32 @@ namespace Misc
|
|||
class Rng
|
||||
{
|
||||
public:
|
||||
class Seed
|
||||
{
|
||||
std::mt19937 mGenerator;
|
||||
public:
|
||||
Seed();
|
||||
Seed(const Seed&) = delete;
|
||||
Seed(unsigned int seed);
|
||||
friend class Rng;
|
||||
};
|
||||
|
||||
/// create a RNG
|
||||
static std::mt19937 generator;
|
||||
static Seed& getSeed();
|
||||
|
||||
/// seed the RNG
|
||||
static void init(unsigned int seed = generateDefaultSeed());
|
||||
|
||||
/// return value in range [0.0f, 1.0f) <- note open upper range.
|
||||
static float rollProbability();
|
||||
static float rollProbability(Seed& seed = getSeed());
|
||||
|
||||
/// return value in range [0.0f, 1.0f] <- note closed upper range.
|
||||
static float rollClosedProbability();
|
||||
static float rollClosedProbability(Seed& seed = getSeed());
|
||||
|
||||
/// return value in range [0, max) <- note open upper range.
|
||||
static int rollDice(int max);
|
||||
static int rollDice(int max, Seed& seed = getSeed());
|
||||
|
||||
/// return value in range [0, 99]
|
||||
static int roll0to99() { return rollDice(100); }
|
||||
static int roll0to99(Seed& seed = getSeed()) { return rollDice(100, seed); }
|
||||
|
||||
/// returns default seed for RNG
|
||||
static unsigned int generateDefaultSeed();
|
||||
|
|
Loading…
Reference in a new issue