mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 22:45:34 +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 #1952: Incorrect particle lighting
|
||||||
Bug #2069: Fireflies in Fireflies invade Morrowind look wrong
|
Bug #2069: Fireflies in Fireflies invade Morrowind look wrong
|
||||||
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
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 #3676: NiParticleColorModifier isn't applied properly
|
||||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
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 #4021: Attributes and skills are not stored as floats
|
||||||
Bug #4055: Local scripts don't inherit variables from their base record
|
Bug #4055: Local scripts don't inherit variables from their base record
|
||||||
Bug #4623: Corprus implementation is incorrect
|
Bug #4623: Corprus implementation is incorrect
|
||||||
|
|
|
@ -31,44 +31,41 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
class ContainerCustomData : public MWWorld::CustomData
|
ContainerCustomData::ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell)
|
||||||
{
|
{
|
||||||
public:
|
unsigned int seed = Misc::Rng::rollDice(std::numeric_limits<int>::max());
|
||||||
MWWorld::ContainerStore mContainerStore;
|
// setting ownership not needed, since taking items from a container inherits the
|
||||||
|
// container's owner automatically
|
||||||
virtual MWWorld::CustomData *clone() const;
|
mStore.fillNonRandom(container.mInventory, "", seed);
|
||||||
|
|
||||||
virtual ContainerCustomData& asContainerCustomData()
|
|
||||||
{
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
virtual const ContainerCustomData& asContainerCustomData() const
|
|
||||||
|
ContainerCustomData::ContainerCustomData(const ESM::InventoryState& inventory)
|
||||||
{
|
{
|
||||||
return *this;
|
mStore.readState(inventory);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
MWWorld::CustomData *ContainerCustomData::clone() const
|
MWWorld::CustomData *ContainerCustomData::clone() const
|
||||||
{
|
{
|
||||||
return new ContainerCustomData (*this);
|
return new ContainerCustomData (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContainerCustomData& ContainerCustomData::asContainerCustomData()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
const ContainerCustomData& ContainerCustomData::asContainerCustomData() const
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
|
void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
if (!ptr.getRefData().getCustomData())
|
||||||
{
|
{
|
||||||
std::unique_ptr<ContainerCustomData> data (new ContainerCustomData);
|
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
|
||||||
|
|
||||||
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, "");
|
|
||||||
|
|
||||||
// store
|
// 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());
|
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
|
void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
||||||
{
|
{
|
||||||
if (!model.empty()) {
|
if (!model.empty()) {
|
||||||
|
@ -228,12 +214,12 @@ namespace MWClass
|
||||||
return !name.empty() ? name : ref->mBase->mId;
|
return !name.empty() ? name : ref->mBase->mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr)
|
MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) const
|
||||||
const
|
|
||||||
{
|
{
|
||||||
ensureCustomData (ptr);
|
ensureCustomData (ptr);
|
||||||
|
auto& data = ptr.getRefData().getCustomData()->asContainerCustomData();
|
||||||
return ptr.getRefData().getCustomData()->asContainerCustomData().mContainerStore;
|
data.mStore.mPtr = ptr;
|
||||||
|
return data.mStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Container::getScript (const MWWorld::ConstPtr& ptr) const
|
std::string Container::getScript (const MWWorld::ConstPtr& ptr) const
|
||||||
|
@ -253,8 +239,7 @@ namespace MWClass
|
||||||
bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const
|
bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
|
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
|
||||||
return !canBeHarvested(ptr) || data->asContainerCustomData().mContainerStore.hasVisibleItems();
|
return !canBeHarvested(ptr) || data->asContainerCustomData().mStore.hasVisibleItems();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,28 +302,20 @@ namespace MWClass
|
||||||
if (!state.mHasCustomState)
|
if (!state.mHasCustomState)
|
||||||
return;
|
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();
|
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
|
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;
|
state.mHasCustomState = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
|
|
||||||
ESM::ContainerState& containerState = state.asContainerState();
|
ESM::ContainerState& containerState = state.asContainerState();
|
||||||
customData.mContainerStore.writeState (containerState.mInventory);
|
customData.mStore.writeState (containerState.mInventory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,32 @@
|
||||||
#define GAME_MWCLASS_CONTAINER_H
|
#define GAME_MWCLASS_CONTAINER_H
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
#include "../mwworld/customdata.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct Container;
|
||||||
|
struct InventoryState;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWClass
|
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
|
class Container : public MWWorld::Class
|
||||||
{
|
{
|
||||||
void ensureCustomData (const MWWorld::Ptr& ptr) const;
|
void ensureCustomData (const MWWorld::Ptr& ptr) const;
|
||||||
|
@ -60,8 +83,6 @@ namespace MWClass
|
||||||
|
|
||||||
virtual void respawn (const MWWorld::Ptr& ptr) const;
|
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 std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
||||||
virtual bool useAnim() 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
|
int Creature::getBaseFightRating(const MWWorld::ConstPtr &ptr) const
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
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 respawn (const MWWorld::Ptr& ptr) const;
|
||||||
|
|
||||||
virtual void restock (const MWWorld::Ptr &ptr) const;
|
|
||||||
|
|
||||||
virtual int getBaseFightRating(const MWWorld::ConstPtr &ptr) const;
|
virtual int getBaseFightRating(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
||||||
virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) 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
|
int Npc::getBaseFightRating (const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
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 respawn (const MWWorld::Ptr& ptr) const;
|
||||||
|
|
||||||
virtual void restock (const MWWorld::Ptr& ptr) const;
|
|
||||||
|
|
||||||
virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const;
|
virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const;
|
||||||
|
|
||||||
virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -168,6 +168,7 @@ namespace MWGui
|
||||||
|
|
||||||
if (!mPtr.isEmpty())
|
if (!mPtr.isEmpty())
|
||||||
MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);
|
MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);
|
||||||
|
resetReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
||||||
|
|
|
@ -39,22 +39,29 @@ namespace
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems)
|
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
|
bool ContainerItemModel::allowedToUseItems() const
|
||||||
{
|
{
|
||||||
if (mItemSources.size() == 0)
|
if (mItemSources.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
MWWorld::Ptr ptr = MWMechanics::getPlayer();
|
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
|
// Check if the player is allowed to use items from opened container
|
||||||
MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();
|
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)
|
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)
|
MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
|
auto& source = mItemSources[0];
|
||||||
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
|
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!");
|
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)
|
void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
|
||||||
{
|
{
|
||||||
int toRemove = 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)
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||||
{
|
{
|
||||||
if (stacks(*it, item.mBase))
|
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)
|
if (toRemove <= 0)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -138,9 +151,9 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
|
||||||
void ContainerItemModel::update()
|
void ContainerItemModel::update()
|
||||||
{
|
{
|
||||||
mItems.clear();
|
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)
|
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())
|
if (mItemSources.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MWWorld::Ptr target = mItemSources[0];
|
MWWorld::Ptr target = mItemSources[0].first;
|
||||||
|
|
||||||
if (target.getTypeName() != typeid(ESM::Container).name())
|
if (target.getTypeName() != typeid(ESM::Container).name())
|
||||||
return true;
|
return true;
|
||||||
|
@ -224,7 +237,7 @@ bool ContainerItemModel::onTakeItem(const MWWorld::Ptr &item, int count)
|
||||||
if (mItemSources.empty())
|
if (mItemSources.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MWWorld::Ptr target = mItemSources[0];
|
MWWorld::Ptr target = mItemSources[0].first;
|
||||||
|
|
||||||
// Looting a dead corpse is considered OK
|
// Looting a dead corpse is considered OK
|
||||||
if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
|
if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
#ifndef MWGUI_CONTAINER_ITEM_MODEL_H
|
#ifndef MWGUI_CONTAINER_ITEM_MODEL_H
|
||||||
#define MWGUI_CONTAINER_ITEM_MODEL_H
|
#define MWGUI_CONTAINER_ITEM_MODEL_H
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "itemmodel.hpp"
|
#include "itemmodel.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -32,9 +37,9 @@ namespace MWGui
|
||||||
virtual void update();
|
virtual void update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<MWWorld::Ptr> mItemSources;
|
std::vector<std::pair<MWWorld::Ptr, MWWorld::ResolutionHandle>> mItemSources;
|
||||||
std::vector<MWWorld::Ptr> mWorldItems;
|
std::vector<MWWorld::Ptr> mWorldItems;
|
||||||
|
const bool mTrading;
|
||||||
std::vector<ItemStack> mItems;
|
std::vector<ItemStack> mItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace MWGui
|
||||||
mBorrowedToUs.clear();
|
mBorrowedToUs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ItemStack> TradeItemModel::getItemsBorrowedToUs()
|
const std::vector<ItemStack> TradeItemModel::getItemsBorrowedToUs() const
|
||||||
{
|
{
|
||||||
return mBorrowedToUs;
|
return mBorrowedToUs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace MWGui
|
||||||
/// and removing weight for items we've lent to someone else.
|
/// and removing weight for items we've lent to someone else.
|
||||||
void adjustEncumbrance (float& encumbrance);
|
void adjustEncumbrance (float& encumbrance);
|
||||||
|
|
||||||
std::vector<ItemStack> getItemsBorrowedToUs();
|
const std::vector<ItemStack> getItemsBorrowedToUs() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void borrowImpl(const ItemStack& item, std::vector<ItemStack>& out);
|
void borrowImpl(const ItemStack& item, std::vector<ItemStack>& out);
|
||||||
|
|
|
@ -99,20 +99,6 @@ namespace MWGui
|
||||||
setCoord(400, 0, 400, 300);
|
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)
|
void TradeWindow::setPtr(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
mPtr = actor;
|
mPtr = actor;
|
||||||
|
@ -121,10 +107,10 @@ namespace MWGui
|
||||||
mCurrentMerchantOffer = 0;
|
mCurrentMerchantOffer = 0;
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> itemSources;
|
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);
|
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;
|
std::vector<MWWorld::Ptr> worldItems;
|
||||||
MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems);
|
MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems);
|
||||||
|
|
||||||
|
@ -281,8 +267,8 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
// were there any items traded at all?
|
// were there any items traded at all?
|
||||||
std::vector<ItemStack> playerBought = playerItemModel->getItemsBorrowedToUs();
|
const std::vector<ItemStack>& playerBought = playerItemModel->getItemsBorrowedToUs();
|
||||||
std::vector<ItemStack> merchantBought = mTradeModel->getItemsBorrowedToUs();
|
const std::vector<ItemStack>& merchantBought = mTradeModel->getItemsBorrowedToUs();
|
||||||
if (playerBought.empty() && merchantBought.empty())
|
if (playerBought.empty() && merchantBought.empty())
|
||||||
{
|
{
|
||||||
// user notification
|
// user notification
|
||||||
|
@ -313,7 +299,7 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the player is attempting to sell back an item stolen from this actor
|
// 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))
|
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()->playSound("Item Gold Up");
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
|
||||||
|
|
||||||
restock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TradeWindow::onAccept(MyGUI::EditBox *sender)
|
void TradeWindow::onAccept(MyGUI::EditBox *sender)
|
||||||
|
@ -478,7 +462,7 @@ namespace MWGui
|
||||||
// connected to buying and selling the same item.
|
// connected to buying and selling the same item.
|
||||||
// This value has been determined by researching the limitations of the vanilla formula
|
// This value has been determined by researching the limitations of the vanilla formula
|
||||||
// and may not be sufficient if getBarterOffer behavior has been changed.
|
// 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)
|
for (const ItemStack& itemStack : playerBorrowed)
|
||||||
{
|
{
|
||||||
const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
|
const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
|
||||||
|
@ -487,7 +471,7 @@ namespace MWGui
|
||||||
merchantOffer -= std::max(cap, buyingPrice);
|
merchantOffer -= std::max(cap, buyingPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ItemStack> merchantBorrowed = mTradeModel->getItemsBorrowedToUs();
|
const std::vector<ItemStack>& merchantBorrowed = mTradeModel->getItemsBorrowedToUs();
|
||||||
for (const ItemStack& itemStack : merchantBorrowed)
|
for (const ItemStack& itemStack : merchantBorrowed)
|
||||||
{
|
{
|
||||||
const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
|
const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
|
||||||
|
@ -532,4 +516,9 @@ namespace MWGui
|
||||||
mTradeModel = nullptr;
|
mTradeModel = nullptr;
|
||||||
mSortModel = nullptr;
|
mSortModel = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TradeWindow::onClose()
|
||||||
|
{
|
||||||
|
resetReference();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace MWGui
|
||||||
|
|
||||||
void setPtr(const MWWorld::Ptr& actor);
|
void setPtr(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
virtual void onClose() override;
|
||||||
void onFrame(float dt);
|
void onFrame(float dt);
|
||||||
void clear() { resetReference(); }
|
void clear() { resetReference(); }
|
||||||
|
|
||||||
|
@ -111,8 +112,6 @@ namespace MWGui
|
||||||
virtual void onReferenceUnavailable();
|
virtual void onReferenceUnavailable();
|
||||||
|
|
||||||
int getMerchantGold();
|
int getMerchantGold();
|
||||||
|
|
||||||
void restock();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,14 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
/// @return ID of resulting item, or empty if none
|
/// @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 std::vector<ESM::LevelledListBase::LevelItem>& items = levItem->mList;
|
||||||
|
|
||||||
const MWWorld::Ptr& player = getPlayer();
|
const MWWorld::Ptr& player = getPlayer();
|
||||||
int playerLevel = player.getClass().getCreatureStats(player).getLevel();
|
int playerLevel = player.getClass().getCreatureStats(player).getLevel();
|
||||||
|
|
||||||
if (Misc::Rng::roll0to99() < levItem->mChanceNone)
|
if (Misc::Rng::roll0to99(seed) < levItem->mChanceNone)
|
||||||
return std::string();
|
return std::string();
|
||||||
|
|
||||||
std::vector<std::string> candidates;
|
std::vector<std::string> candidates;
|
||||||
|
@ -55,7 +55,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
if (candidates.empty())
|
if (candidates.empty())
|
||||||
return std::string();
|
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
|
// Vanilla doesn't fail on nonexistent items in levelled lists
|
||||||
if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item)))
|
if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item)))
|
||||||
|
@ -74,9 +74,9 @@ namespace MWMechanics
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name())
|
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
|
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)
|
void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer)
|
||||||
{
|
{
|
||||||
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
|
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
|
||||||
|
MWWorld::ContainerStore& containerStore = targetContainer.getClass().getContainerStore(targetContainer);
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||||
{
|
{
|
||||||
StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getCellRef().getRefId()));
|
StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getCellRef().getRefId()));
|
||||||
|
@ -1065,7 +1066,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
int toMove = it->getRefData().getCount() - itemCount;
|
int toMove = it->getRefData().getCount() - itemCount;
|
||||||
|
|
||||||
targetContainer.getClass().getContainerStore(targetContainer).add(*it, toMove, targetContainer);
|
containerStore.add(*it, toMove, targetContainer);
|
||||||
store.remove(*it, toMove, player);
|
store.remove(*it, toMove, player);
|
||||||
}
|
}
|
||||||
// TODO: unhardcode the locklevel
|
// TODO: unhardcode the locklevel
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace MWWorld
|
||||||
|
|
||||||
MWWorld::Ptr target = getTarget();
|
MWWorld::Ptr target = getTarget();
|
||||||
MWWorld::ContainerStore& store = target.getClass().getContainerStore (target);
|
MWWorld::ContainerStore& store = target.getClass().getContainerStore (target);
|
||||||
|
store.resolve();
|
||||||
MWWorld::ContainerStore& actorStore = actor.getClass().getContainerStore(actor);
|
MWWorld::ContainerStore& actorStore = actor.getClass().getContainerStore(actor);
|
||||||
std::map<std::string, int> takenMap;
|
std::map<std::string, int> takenMap;
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
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)
|
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
|
||||||
{
|
{
|
||||||
Ptr ptr = getCurrentPtr(&*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);
|
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,8 +353,6 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual void respawn (const MWWorld::Ptr& ptr) const {}
|
virtual void respawn (const MWWorld::Ptr& ptr) const {}
|
||||||
|
|
||||||
virtual void restock (const MWWorld::Ptr& ptr) const {}
|
|
||||||
|
|
||||||
/// Returns sound id
|
/// Returns sound id
|
||||||
virtual std::string getSound(const MWWorld::ConstPtr& ptr) const;
|
virtual std::string getSound(const MWWorld::ConstPtr& ptr) const;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,21 @@
|
||||||
|
|
||||||
namespace
|
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>
|
template<typename T>
|
||||||
float getTotalWeight (const MWWorld::CellRefList<T>& cellRefList)
|
float getTotalWeight (const MWWorld::CellRefList<T>& cellRefList)
|
||||||
{
|
{
|
||||||
|
@ -44,6 +59,7 @@ namespace
|
||||||
MWWorld::Ptr searchId (MWWorld::CellRefList<T>& list, const std::string& id,
|
MWWorld::Ptr searchId (MWWorld::CellRefList<T>& list, const std::string& id,
|
||||||
MWWorld::ContainerStore *store)
|
MWWorld::ContainerStore *store)
|
||||||
{
|
{
|
||||||
|
store->resolve();
|
||||||
std::string id2 = Misc::StringUtils::lowerCase (id);
|
std::string id2 = Misc::StringUtils::lowerCase (id);
|
||||||
|
|
||||||
for (typename MWWorld::CellRefList<T>::List::iterator iter (list.mList.begin());
|
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>
|
template<typename T>
|
||||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList<T>& collection,
|
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList<T>& collection,
|
||||||
const ESM::ObjectState& state)
|
const ESM::ObjectState& state)
|
||||||
|
@ -119,7 +147,11 @@ MWWorld::ContainerStore::ContainerStore()
|
||||||
: mListener(nullptr)
|
: mListener(nullptr)
|
||||||
, mRechargingItemsUpToDate(false)
|
, mRechargingItemsUpToDate(false)
|
||||||
, mCachedWeight (0)
|
, mCachedWeight (0)
|
||||||
, mWeightUpToDate (false) {}
|
, mWeightUpToDate (false)
|
||||||
|
, mModified(false)
|
||||||
|
, mResolved(false)
|
||||||
|
, mSeed()
|
||||||
|
, mPtr() {}
|
||||||
|
|
||||||
MWWorld::ContainerStore::~ContainerStore() {}
|
MWWorld::ContainerStore::~ContainerStore() {}
|
||||||
|
|
||||||
|
@ -153,22 +185,12 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()
|
||||||
return ContainerStoreIterator (this);
|
return ContainerStoreIterator (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
int MWWorld::ContainerStore::count(const std::string &id)
|
int MWWorld::ContainerStore::count(const std::string &id) const
|
||||||
{
|
{
|
||||||
int total=0;
|
int total=0;
|
||||||
for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
|
for (const auto& iter : *this)
|
||||||
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
|
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
|
||||||
total += iter->getRefData().getCount();
|
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();
|
|
||||||
return total;
|
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)
|
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container, int count)
|
||||||
{
|
{
|
||||||
|
resolve();
|
||||||
if (ptr.getRefData().getCount() <= count)
|
if (ptr.getRefData().getCount() <= count)
|
||||||
return end();
|
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);
|
const std::string script = it->getClass().getScript(*it);
|
||||||
if (!script.empty())
|
if (!script.empty())
|
||||||
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it);
|
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)
|
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item)
|
||||||
{
|
{
|
||||||
|
resolve();
|
||||||
MWWorld::ContainerStoreIterator retval = end();
|
MWWorld::ContainerStoreIterator retval = end();
|
||||||
for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)
|
for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)
|
||||||
{
|
{
|
||||||
|
@ -216,7 +240,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::
|
||||||
{
|
{
|
||||||
if (stacks(*iter, item))
|
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);
|
item.getRefData().setCount(0);
|
||||||
retval = iter;
|
retval = iter;
|
||||||
break;
|
break;
|
||||||
|
@ -328,8 +352,10 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
|
||||||
return it;
|
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);
|
int type = getType(ptr);
|
||||||
|
|
||||||
const MWWorld::ESMStore &esmStore =
|
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))
|
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();
|
flagAsModified();
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
@ -361,7 +387,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr,
|
||||||
if (stacks(*iter, ptr))
|
if (stacks(*iter, ptr))
|
||||||
{
|
{
|
||||||
// stack
|
// stack
|
||||||
iter->getRefData().setCount( iter->getRefData().getCount() + count );
|
iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));
|
||||||
|
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
return iter;
|
return iter;
|
||||||
|
@ -439,6 +465,7 @@ void MWWorld::ContainerStore::updateRechargingItems()
|
||||||
|
|
||||||
int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
|
int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
|
||||||
{
|
{
|
||||||
|
resolve();
|
||||||
int toRemove = count;
|
int toRemove = count;
|
||||||
|
|
||||||
for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)
|
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)
|
int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor)
|
||||||
{
|
{
|
||||||
assert(this == item.getContainerStore());
|
assert(this == item.getContainerStore());
|
||||||
|
resolve();
|
||||||
|
|
||||||
int toRemove = count;
|
int toRemove = count;
|
||||||
RefData& itemRef = item.getRefData();
|
RefData& itemRef = item.getRefData();
|
||||||
|
@ -476,7 +504,7 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
itemRef.setCount(itemRef.getCount() - toRemove);
|
itemRef.setCount(subtractItems(itemRef.getCount(false), toRemove));
|
||||||
toRemove = 0;
|
toRemove = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,20 +518,33 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
|
||||||
return count - toRemove;
|
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();
|
for (const ESM::ContItem& iter : items.mList)
|
||||||
++iter)
|
|
||||||
{
|
{
|
||||||
std::string id = Misc::StringUtils::lowerCase(iter->mItem);
|
std::string id = Misc::StringUtils::lowerCase(iter.mItem);
|
||||||
addInitialItem(id, owner, iter->mCount);
|
addInitialItem(id, owner, iter.mCount, &seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
|
mResolved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner,
|
void MWWorld::ContainerStore::fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed)
|
||||||
int count, bool topLevel, const std::string& levItem)
|
{
|
||||||
|
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.
|
if (count == 0) return; //Don't restock with nothing.
|
||||||
try
|
try
|
||||||
|
@ -511,13 +552,13 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
|
||||||
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
|
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
|
||||||
if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())
|
if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())
|
||||||
{
|
{
|
||||||
addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem);
|
addInitialItemImp(ref.getPtr(), owner, count, seed, topLevel, levItem);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Adding just one item per time to make sure there isn't a stack of scripted items
|
// 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++)
|
for (int i = 0; i < std::abs(count); i++)
|
||||||
addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem);
|
addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, seed, topLevel, levItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
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,
|
void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner, int count,
|
||||||
int count, bool topLevel, const std::string& levItem)
|
Misc::Rng::Seed* seed, bool topLevel, const std::string& levItem)
|
||||||
{
|
{
|
||||||
if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())
|
if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())
|
||||||
{
|
{
|
||||||
|
if(!seed)
|
||||||
|
return;
|
||||||
const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;
|
const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;
|
||||||
|
|
||||||
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
|
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
|
||||||
{
|
{
|
||||||
for (int i=0; i<std::abs(count); ++i)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
else
|
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())
|
if (itemId.empty())
|
||||||
return;
|
return;
|
||||||
addInitialItem(itemId, owner, count, false, levItemList->mId);
|
addInitialItem(itemId, owner, count, seed, false, levItemList->mId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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);
|
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()
|
void MWWorld::ContainerStore::clear()
|
||||||
{
|
{
|
||||||
for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
|
for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
|
||||||
iter->getRefData().setCount (0);
|
iter->getRefData().setCount (0);
|
||||||
|
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
|
mModified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::ContainerStore::flagAsModified()
|
void MWWorld::ContainerStore::flagAsModified()
|
||||||
|
@ -665,6 +612,45 @@ void MWWorld::ContainerStore::flagAsModified()
|
||||||
mRechargingItemsUpToDate = false;
|
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
|
float MWWorld::ContainerStore::getWeight() const
|
||||||
{
|
{
|
||||||
if (!mWeightUpToDate)
|
if (!mWeightUpToDate)
|
||||||
|
@ -761,6 +747,7 @@ MWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)
|
||||||
|
|
||||||
MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
|
MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
|
||||||
{
|
{
|
||||||
|
resolve();
|
||||||
{
|
{
|
||||||
Ptr ptr = searchId (potions, id, this);
|
Ptr ptr = searchId (potions, id, this);
|
||||||
if (!ptr.isEmpty())
|
if (!ptr.isEmpty())
|
||||||
|
@ -836,6 +823,22 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
|
||||||
return Ptr();
|
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
|
void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
|
||||||
{
|
{
|
||||||
state.mItems.clear();
|
state.mItems.clear();
|
||||||
|
@ -853,13 +856,13 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
|
||||||
storeStates (repairs, state, index);
|
storeStates (repairs, state, index);
|
||||||
storeStates (weapons, state, index, true);
|
storeStates (weapons, state, index, true);
|
||||||
storeStates (lights, state, index, true);
|
storeStates (lights, state, index, true);
|
||||||
|
|
||||||
state.mLevelledItemMap = mLevelledItemMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
|
void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
mModified = true;
|
||||||
|
mResolved = true;
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (std::vector<ESM::ObjectState>::const_iterator
|
for (std::vector<ESM::ObjectState>::const_iterator
|
||||||
|
@ -893,9 +896,6 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mLevelledItemMap = inventory.mLevelledItemMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class PtrType>
|
template<class PtrType>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <components/esm/loadalch.hpp>
|
#include <components/esm/loadalch.hpp>
|
||||||
|
@ -18,6 +19,8 @@
|
||||||
#include <components/esm/loadrepa.hpp>
|
#include <components/esm/loadrepa.hpp>
|
||||||
#include <components/esm/loadweap.hpp>
|
#include <components/esm/loadweap.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include "ptr.hpp"
|
#include "ptr.hpp"
|
||||||
#include "cellreflist.hpp"
|
#include "cellreflist.hpp"
|
||||||
|
|
||||||
|
@ -27,6 +30,11 @@ namespace ESM
|
||||||
struct InventoryState;
|
struct InventoryState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWClass
|
||||||
|
{
|
||||||
|
class Container;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class ContainerStore;
|
class ContainerStore;
|
||||||
|
@ -37,6 +45,21 @@ namespace MWWorld
|
||||||
typedef ContainerStoreIteratorBase<Ptr> ContainerStoreIterator;
|
typedef ContainerStoreIteratorBase<Ptr> ContainerStoreIterator;
|
||||||
typedef ContainerStoreIteratorBase<ConstPtr> ConstContainerStoreIterator;
|
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
|
class ContainerStoreListener
|
||||||
{
|
{
|
||||||
|
@ -93,15 +116,18 @@ namespace MWWorld
|
||||||
MWWorld::CellRefList<ESM::Repair> repairs;
|
MWWorld::CellRefList<ESM::Repair> repairs;
|
||||||
MWWorld::CellRefList<ESM::Weapon> weapons;
|
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 float mCachedWeight;
|
||||||
mutable bool mWeightUpToDate;
|
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 = "");
|
bool mModified;
|
||||||
void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
|
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>
|
template<typename T>
|
||||||
ContainerStoreIterator getState (CellRefList<T>& collection,
|
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.
|
/// 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.
|
/// @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?
|
///< @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;
|
ContainerStoreListener* getContListener() const;
|
||||||
void setContListener(ContainerStoreListener* listener);
|
void setContListener(ContainerStoreListener* listener);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ContainerStoreIterator addNewStack (const ConstPtr& ptr, int count);
|
ContainerStoreIterator addNewStack (const ConstPtr& ptr, int count);
|
||||||
///< Add the item to this container (do not try to stack it onto existing items)
|
///< Add the item to this container (do not try to stack it onto existing items)
|
||||||
|
|
||||||
virtual void flagAsModified();
|
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:
|
public:
|
||||||
|
|
||||||
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
||||||
///< @return true if the two specified objects can stack with each other
|
///< @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.
|
///< 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();
|
virtual void clear();
|
||||||
///< Empty container.
|
///< Empty container.
|
||||||
|
@ -220,8 +246,15 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual void readState (const ESM::InventoryState& state);
|
virtual void readState (const ESM::InventoryState& state);
|
||||||
|
|
||||||
|
bool isResolved() const;
|
||||||
|
|
||||||
|
void resolve();
|
||||||
|
ResolutionHandle resolveTemporarily();
|
||||||
|
|
||||||
friend class ContainerStoreIteratorBase<Ptr>;
|
friend class ContainerStoreIteratorBase<Ptr>;
|
||||||
friend class ContainerStoreIteratorBase<ConstPtr>;
|
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
|
// unstack if required
|
||||||
if (!allowedSlots.second && iter->getRefData().getCount() > 1)
|
if (!allowedSlots.second && iter->getRefData().getCount() > 1)
|
||||||
{
|
{
|
||||||
MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1);
|
int count = iter->getRefData().getCount(false);
|
||||||
iter->getRefData().setCount(iter->getRefData().getCount()-1);
|
MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, count > 0 ? 1 : -1);
|
||||||
|
iter->getRefData().setCount(subtractItems(count, 1));
|
||||||
mSlots[slot] = newIter;
|
mSlots[slot] = newIter;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -850,8 +851,8 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(con
|
||||||
{
|
{
|
||||||
if (stacks(*iter, item) && !isEquipped(*iter))
|
if (stacks(*iter, item) && !isEquipped(*iter))
|
||||||
{
|
{
|
||||||
iter->getRefData().setCount(iter->getRefData().getCount() + count);
|
iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));
|
||||||
item.getRefData().setCount(item.getRefData().getCount() - count);
|
item.getRefData().setCount(subtractItems(item.getRefData().getCount(false), count));
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace
|
||||||
// Ignore containers without generated content
|
// Ignore containers without generated content
|
||||||
if (containerPtr.getTypeName() == typeid(ESM::Container).name() &&
|
if (containerPtr.getTypeName() == typeid(ESM::Container).name() &&
|
||||||
containerPtr.getRefData().getCustomData() == nullptr)
|
containerPtr.getRefData().getCustomData() == nullptr)
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr);
|
MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr);
|
||||||
for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
|
for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
|
||||||
|
|
|
@ -146,8 +146,10 @@ namespace MWWorld
|
||||||
return mBaseNode;
|
return mBaseNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RefData::getCount() const
|
int RefData::getCount(bool absolute) const
|
||||||
{
|
{
|
||||||
|
if(absolute)
|
||||||
|
return std::abs(mCount);
|
||||||
return mCount;
|
return mCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ namespace MWWorld
|
||||||
/// Set base node (can be a null pointer).
|
/// Set base node (can be a null pointer).
|
||||||
void setBaseNode (SceneUtil::PositionAttitudeTransform* base);
|
void setBaseNode (SceneUtil::PositionAttitudeTransform* base);
|
||||||
|
|
||||||
int getCount() const;
|
int getCount(bool absolute = true) const;
|
||||||
|
|
||||||
void setLocals (const ESM::Script& script);
|
void setLocals (const ESM::Script& script);
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace DetourNavigator
|
||||||
dtPolyRef resultRef = 0;
|
dtPolyRef resultRef = 0;
|
||||||
osg::Vec3f resultPosition;
|
osg::Vec3f resultPosition;
|
||||||
navMeshQuery.findRandomPointAroundCircle(startRef, start.ptr(), maxRadius, &queryFilter,
|
navMeshQuery.findRandomPointAroundCircle(startRef, start.ptr(), maxRadius, &queryFilter,
|
||||||
&Misc::Rng::rollProbability, &resultRef, resultPosition.ptr());
|
[]() { return Misc::Rng::rollProbability(); }, &resultRef, resultPosition.ptr());
|
||||||
|
|
||||||
if (resultRef == 0)
|
if (resultRef == 0)
|
||||||
return boost::optional<osg::Vec3f>();
|
return boost::optional<osg::Vec3f>();
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
void ESM::InventoryState::load (ESMReader &esm)
|
void ESM::InventoryState::load (ESMReader &esm)
|
||||||
{
|
{
|
||||||
// obsolete
|
// obsolete
|
||||||
|
@ -106,6 +108,19 @@ void ESM::InventoryState::load (ESMReader &esm)
|
||||||
|
|
||||||
mSelectedEnchantItem = -1;
|
mSelectedEnchantItem = -1;
|
||||||
esm.getHNOT(mSelectedEnchantItem, "SELE");
|
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
|
void ESM::InventoryState::save (ESMWriter &esm) const
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
||||||
int ESM::SavedGame::sCurrentFormat = 14;
|
int ESM::SavedGame::sCurrentFormat = 15;
|
||||||
|
|
||||||
void ESM::SavedGame::load (ESMReader &esm)
|
void ESM::SavedGame::load (ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,29 +3,44 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
Misc::Rng::Seed sSeed;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Misc
|
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)
|
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()
|
unsigned int Rng::generateDefaultSeed()
|
||||||
|
|
|
@ -13,24 +13,32 @@ namespace Misc
|
||||||
class Rng
|
class Rng
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
class Seed
|
||||||
|
{
|
||||||
|
std::mt19937 mGenerator;
|
||||||
|
public:
|
||||||
|
Seed();
|
||||||
|
Seed(const Seed&) = delete;
|
||||||
|
Seed(unsigned int seed);
|
||||||
|
friend class Rng;
|
||||||
|
};
|
||||||
|
|
||||||
/// create a RNG
|
static Seed& getSeed();
|
||||||
static std::mt19937 generator;
|
|
||||||
|
|
||||||
/// seed the RNG
|
/// seed the RNG
|
||||||
static void init(unsigned int seed = generateDefaultSeed());
|
static void init(unsigned int seed = generateDefaultSeed());
|
||||||
|
|
||||||
/// return value in range [0.0f, 1.0f) <- note open upper range.
|
/// 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.
|
/// 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.
|
/// 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]
|
/// 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
|
/// returns default seed for RNG
|
||||||
static unsigned int generateDefaultSeed();
|
static unsigned int generateDefaultSeed();
|
||||||
|
|
Loading…
Reference in a new issue