1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-30 04:06:43 +00:00

Merge branch 'radioactive' into 'master'

Container base record mutations

See merge request 

(cherry picked from commit 8b33765dd414680f0074b3e115b52b291b4cb7cb)

275908a0 mutate container base records
16fca11d add changelog entry
This commit is contained in:
psi29a 2020-10-20 09:22:43 +00:00
parent db126a2688
commit 09373a757d
14 changed files with 181 additions and 22 deletions

View file

@ -6,6 +6,7 @@
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 #2798: Mutable ESM records
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

View file

@ -36,6 +36,7 @@ namespace ESM
struct Position;
struct Cell;
struct Class;
struct Container;
struct Creature;
struct Potion;
struct Spell;
@ -385,6 +386,10 @@ namespace MWBase
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
virtual const ESM::Container *createOverrideRecord (const ESM::Container& record) = 0;
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
virtual void update (float duration, bool paused) = 0;
virtual void updatePhysics (float duration, bool paused) = 0;
@ -641,6 +646,8 @@ namespace MWBase
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0;
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
virtual std::vector<MWWorld::Ptr> getAll(const std::string& id) = 0;
};
}

View file

@ -27,6 +27,7 @@
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/npcstats.hpp"
namespace MWClass
@ -290,6 +291,11 @@ namespace MWClass
return !(ref->mBase->mFlags & ESM::Container::Organic);
}
void Container::modifyBaseInventory(const std::string& containerId, const std::string& itemId, int amount) const
{
MWMechanics::modifyBaseInventory<ESM::Container>(containerId, itemId, amount);
}
MWWorld::Ptr Container::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const
{
const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();

View file

@ -84,6 +84,8 @@ namespace MWClass
std::string getModel(const MWWorld::ConstPtr &ptr) const override;
bool useAnim() const override;
void modifyBaseInventory(const std::string& containerId, const std::string& itemId, int amount) const override;
};
}

View file

@ -3,6 +3,7 @@
#include <algorithm>
#include <components/esm/loadcont.hpp>
#include <components/esm/loadcrea.hpp>
#include <components/esm/loadnpc.hpp>
@ -83,6 +84,7 @@ namespace MWMechanics
template void setBaseAISetting<ESM::NPC>(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value);
template void modifyBaseInventory<ESM::Creature>(const std::string& actorId, const std::string& itemId, int amount);
template void modifyBaseInventory<ESM::NPC>(const std::string& actorId, const std::string& itemId, int amount);
template void modifyBaseInventory<ESM::Container>(const std::string& containerId, const std::string& itemId, int amount);
}
#endif

View file

@ -19,15 +19,35 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwclass/container.hpp"
#include "../mwworld/action.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "ref.hpp"
namespace
{
void addToStore(MWWorld::Ptr& itemPtr, int count, MWWorld::Ptr& ptr, MWWorld::ContainerStore& store, bool resolve = true)
{
if (itemPtr.getClass().getScript(itemPtr).empty())
{
store.add (itemPtr, count, ptr, true, resolve);
}
else
{
// Adding just one item per time to make sure there isn't a stack of scripted items
for (int i = 0; i < count; i++)
store.add (itemPtr, 1, ptr, true, resolve);
}
}
}
namespace MWScript
{
namespace Container
@ -60,6 +80,13 @@ namespace MWScript
|| ::Misc::StringUtils::ciEqual(item, "gold_100"))
item = "gold_001";
// Check if "item" can be placed in a container
MWWorld::ManualRef manualRef(MWBase::Environment::get().getWorld()->getStore(), item, 1);
MWWorld::Ptr itemPtr = manualRef.getPtr();
bool isLevelledList = itemPtr.getClass().getTypeName() == typeid(ESM::ItemLevList).name();
if(!isLevelledList)
MWWorld::ContainerStore::getType(itemPtr);
// Explicit calls to non-unique actors affect the base record
if(!R::implicit && ptr.getClass().isActor() && MWBase::Environment::get().getWorld()->getStore().getRefCount(ptr.getCellRef().getRefId()) > 1)
{
@ -67,19 +94,36 @@ namespace MWScript
return;
}
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr);
// Create a Ptr for the first added item to recover the item name later
MWWorld::Ptr itemPtr = *store.add (item, 1, ptr);
if (itemPtr.getClass().getScript(itemPtr).empty())
// Calls to unresolved containers affect the base record
if(ptr.getClass().getTypeName() == typeid(ESM::Container).name() && (!ptr.getRefData().getCustomData() ||
!ptr.getClass().getContainerStore(ptr).isResolved()))
{
store.add (item, count-1, ptr);
}
else
{
// Adding just one item per time to make sure there isn't a stack of scripted items
for (int i = 1; i < count; i++)
store.add (item, 1, ptr);
ptr.getClass().modifyBaseInventory(ptr.getCellRef().getRefId(), item, count);
const ESM::Container* baseRecord = MWBase::Environment::get().getWorld()->getStore().get<ESM::Container>().find(ptr.getCellRef().getRefId());
const auto& ptrs = MWBase::Environment::get().getWorld()->getAll(ptr.getCellRef().getRefId());
for(const auto& container : ptrs)
{
// use the new base record
container.get<ESM::Container>()->mBase = baseRecord;
if(container.getRefData().getCustomData())
{
auto& store = container.getClass().getContainerStore(container);
if(isLevelledList)
{
if(store.isResolved())
{
// TODO #2404
}
}
else
addToStore(itemPtr, count, ptr, store, store.isResolved());
}
}
return;
}
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
// TODO #2404
addToStore(itemPtr, count, ptr, store);
// Spawn a messagebox (only for items added to player's inventory and if player is talking to someone)
if (ptr == MWBase::Environment::get().getWorld ()->getPlayerPtr() )
@ -160,7 +204,26 @@ namespace MWScript
ptr.getClass().modifyBaseInventory(ptr.getCellRef().getRefId(), item, -count);
return;
}
// Calls to unresolved containers affect the base record instead
else if(ptr.getClass().getTypeName() == typeid(ESM::Container).name() &&
(!ptr.getRefData().getCustomData() || !ptr.getClass().getContainerStore(ptr).isResolved()))
{
ptr.getClass().modifyBaseInventory(ptr.getCellRef().getRefId(), item, -count);
const ESM::Container* baseRecord = MWBase::Environment::get().getWorld()->getStore().get<ESM::Container>().find(ptr.getCellRef().getRefId());
const auto& ptrs = MWBase::Environment::get().getWorld()->getAll(ptr.getCellRef().getRefId());
for(const auto& container : ptrs)
{
container.get<ESM::Container>()->mBase = baseRecord;
if(container.getRefData().getCustomData())
{
auto& store = container.getClass().getContainerStore(container);
// Note that unlike AddItem, RemoveItem only removes from unresolved containers
if(!store.isResolved())
store.remove(item, count, ptr, false);
}
}
return;
}
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr);
std::string itemName;

View file

@ -16,6 +16,50 @@
#include "containerstore.hpp"
#include "cellstore.hpp"
namespace
{
template<class Visitor, class Key>
bool forEachInStore(const std::string& id, Visitor&& visitor, std::map<Key, MWWorld::CellStore>& cellStore)
{
for(auto& cell : cellStore)
{
if(cell.second.getState() == MWWorld::CellStore::State_Unloaded)
cell.second.preload();
if(cell.second.getState() == MWWorld::CellStore::State_Preloaded)
{
if(cell.second.hasId(id))
{
cell.second.load();
}
else
continue;
}
bool cont = cell.second.forEach([&] (MWWorld::Ptr ptr)
{
if(*ptr.getCellRef().getRefIdPtr() == id)
{
return visitor(ptr);
}
return true;
});
if(!cont)
return false;
}
return true;
}
struct PtrCollector
{
std::vector<MWWorld::Ptr> mPtrs;
bool operator()(MWWorld::Ptr ptr)
{
mPtrs.emplace_back(ptr);
return true;
}
};
}
MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)
{
if (cell->mData.mFlags & ESM::Cell::Interior)
@ -330,6 +374,14 @@ void MWWorld::Cells::getInteriorPtrs(const std::string &name, std::vector<MWWorl
}
}
std::vector<MWWorld::Ptr> MWWorld::Cells::getAll(const std::string& id)
{
PtrCollector visitor;
if(forEachInStore(id, visitor, mInteriors))
forEachInStore(id, visitor, mExteriors);
return visitor.mPtrs;
}
int MWWorld::Cells::countSavedGameRecords() const
{
int count = 0;

View file

@ -80,6 +80,8 @@ namespace MWWorld
/// @note name must be lower case
void getInteriorPtrs (const std::string& name, std::vector<MWWorld::Ptr>& out);
std::vector<MWWorld::Ptr> getAll(const std::string& id);
int countSavedGameRecords() const;
void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;

View file

@ -290,11 +290,11 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &
return add(ref.getPtr(), count, actorPtr);
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool /*allowAutoEquip*/)
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool /*allowAutoEquip*/, bool resolve)
{
Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
MWWorld::ContainerStoreIterator it = addImp(itemPtr, count);
MWWorld::ContainerStoreIterator it = addImp(itemPtr, count, resolve);
// The copy of the original item we just made
MWWorld::Ptr item = *it;
@ -463,14 +463,15 @@ 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, bool resolveFirst)
{
resolve();
if(resolveFirst)
resolve();
int toRemove = count;
for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId))
toRemove -= remove(*iter, toRemove, actor);
toRemove -= removeImp(*iter, toRemove, actor);
flagAsModified();
@ -494,6 +495,11 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
assert(this == item.getContainerStore());
resolve();
return removeImp(item, count, actor);
}
int MWWorld::ContainerStore::removeImp(const Ptr& item, int count, const Ptr& actor)
{
int toRemove = count;
RefData& itemRef = item.getRefData();

View file

@ -129,6 +129,8 @@ namespace MWWorld
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 = "");
int removeImp(const Ptr& item, int count, const Ptr& actor);
template<typename T>
ContainerStoreIterator getState (CellRefList<T>& collection,
const ESM::ObjectState& state);
@ -165,7 +167,7 @@ namespace MWWorld
bool hasVisibleItems() const;
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true);
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true);
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
///
/// \note The item pointed to is not required to exist beyond this function call.
@ -178,7 +180,7 @@ namespace MWWorld
ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr);
///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true)
int remove(const std::string& itemId, int count, const Ptr& actor);
int remove(const std::string& itemId, int count, const Ptr& actor, bool resolve = true);
///< Remove \a count item(s) designated by \a itemId from this container.
///
/// @return the number of items actually removed

View file

@ -132,9 +132,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor
return *this;
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip)
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip, bool resolve)
{
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, allowAutoEquip);
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, allowAutoEquip, resolve);
// Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves
if (allowAutoEquip && actorPtr != MWMechanics::getPlayer()

View file

@ -125,7 +125,7 @@ namespace MWWorld
InventoryStore* clone() override { return new InventoryStore(*this); }
ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true) override;
ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true) override;
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
/// Auto-equip items if specific conditions are fulfilled and allowAutoEquip is true (see the implementation).
///

View file

@ -1735,6 +1735,11 @@ namespace MWWorld
return mStore.overrideRecord(record);
}
const ESM::Container *World::createOverrideRecord(const ESM::Container &record)
{
return mStore.overrideRecord(record);
}
const ESM::NPC *World::createRecord(const ESM::NPC &record)
{
bool update = false;
@ -3910,4 +3915,9 @@ namespace MWWorld
ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp();
mRendering->skySetDate(currentDate.mDay, currentDate.mMonth);
}
std::vector<MWWorld::Ptr> World::getAll(const std::string& id)
{
return mCells.getAll(id);
}
}

View file

@ -488,6 +488,10 @@ namespace MWWorld
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
const ESM::Container *createOverrideRecord (const ESM::Container& record) override;
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
void update (float duration, bool paused) override;
void updatePhysics (float duration, bool paused) override;
@ -733,6 +737,8 @@ namespace MWWorld
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;
std::vector<MWWorld::Ptr> getAll(const std::string& id) override;
};
}