1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-28 17:39:42 +00:00

Merge branch 'lua_create' into 'master'

Lua commands to create/move/remove objects; consistent handling of disabled objects (#6726, #6893)

See merge request OpenMW/openmw!2627
This commit is contained in:
psi29a 2023-01-22 13:27:10 +00:00
commit a9dbb023d7
51 changed files with 458 additions and 239 deletions

View file

@ -16,6 +16,7 @@
Bug #6645: Enemy block sounds align with animation instead of blocked hits
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
Bug #6807: Ultimate Galleon is not working properly
Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands
Bug #6939: OpenMW-CS: ID columns are too short
Bug #6949: Sun Damage effect doesn't work in quasi exteriors
Bug #6964: Nerasa Dralor Won't Follow
@ -35,6 +36,7 @@
Bug #7172: Current music playlist continues playing indefinitely if next playlist is empty
Feature #5492: Let rain and snow collide with statics
Feature #6447: Add LOD support to Object Paging
Feature #6726: Lua API for creating new objects
Feature #6922: Improve launcher appearance
Feature #6933: Support high-resolution cursor textures
Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData

View file

@ -170,7 +170,7 @@ namespace MWClass
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId(), prng);
if (hasInventory)
getInventoryStore(ptr).autoEquip(ptr);
getInventoryStore(ptr).autoEquip();
}
}
@ -507,14 +507,16 @@ namespace MWClass
MWWorld::ContainerStore& Creature::getContainerStore(const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
return *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore;
auto& store = *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore;
if (hasInventoryStore(ptr))
static_cast<MWWorld::InventoryStore&>(store).setActor(ptr);
return store;
}
MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr& ptr) const
{
if (hasInventoryStore(ptr))
return dynamic_cast<MWWorld::InventoryStore&>(getContainerStore(ptr));
return static_cast<MWWorld::InventoryStore&>(getContainerStore(ptr));
else
throw std::runtime_error("this creature has no inventory store");
}

View file

@ -17,6 +17,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwworld/worldmodel.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwgui/ustring.hpp"
@ -208,6 +209,7 @@ namespace MWClass
newPtr.getRefData().setCount(count);
}
newPtr.getCellRef().unsetRefNum();
MWBase::Environment::get().getWorldModel()->registerPtr(newPtr);
return newPtr;
}

View file

@ -414,7 +414,7 @@ namespace MWClass
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
getInventoryStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId(), prng);
getInventoryStore(ptr).autoEquip(ptr);
getInventoryStore(ptr).autoEquip();
}
}
@ -862,7 +862,7 @@ namespace MWClass
// Armor broken? unequip it
if (armorhealth == 0)
armor = *inv.unequipItem(armor, ptr);
armor = *inv.unequipItem(armor);
}
if (ptr == MWMechanics::getPlayer())
@ -984,15 +984,17 @@ namespace MWClass
MWWorld::ContainerStore& Npc::getContainerStore(const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;
auto& store = ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;
store.setActor(ptr);
return store;
}
MWWorld::InventoryStore& Npc::getInventoryStore(const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;
auto& store = ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;
store.setActor(ptr);
return store;
}
const ESM::RefId& Npc::getScript(const MWWorld::ConstPtr& ptr) const
@ -1172,7 +1174,7 @@ namespace MWClass
MWMechanics::CastSpell cast(actor, actor);
const ESM::RefId& recordId = consumable.getCellRef().getRefId();
MWBase::Environment::get().getLuaManager()->itemConsumed(consumable, actor);
actor.getClass().getContainerStore(actor).remove(consumable, 1, actor);
actor.getClass().getContainerStore(actor).remove(consumable, 1);
return cast.cast(recordId);
}

View file

@ -571,8 +571,8 @@ namespace MWDialogue
if (gold)
{
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, gold, player);
mActor.getClass().getContainerStore(mActor).add(MWWorld::ContainerStore::sGoldId, gold, mActor);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, gold);
mActor.getClass().getContainerStore(mActor).add(MWWorld::ContainerStore::sGoldId, gold);
}
}

View file

@ -211,7 +211,7 @@ namespace MWGui
if (invStore.isEquipped(item.mBase) == false)
continue;
invStore.unequipItem(item.mBase, mPtr);
invStore.unequipItem(item.mBase);
}
}

View file

@ -105,7 +105,7 @@ namespace MWGui
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 *store.add(item.mBase, count, source.first, allowAutoEquip);
return *store.add(item.mBase, count, allowAutoEquip);
}
void ContainerItemModel::removeItem(const ItemStack& item, size_t count)
@ -125,7 +125,7 @@ namespace MWGui
if (quantity < 0 && mTrading)
toRemove += quantity;
else
toRemove -= store.remove(*it, toRemove, source.first);
toRemove -= store.remove(*it, toRemove);
if (toRemove <= 0)
return;
}

View file

@ -50,7 +50,7 @@ namespace MWGui
{
if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor))
throw std::runtime_error("Item to copy needs to be from a different container!");
return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, allowAutoEquip);
return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, allowAutoEquip);
}
void InventoryItemModel::removeItem(const ItemStack& item, size_t count)
@ -60,12 +60,12 @@ namespace MWGui
if (mActor.getClass().hasInventoryStore(mActor))
{
MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor);
removed = store.remove(item.mBase, count, mActor, true);
removed = store.remove(item.mBase, count, true);
}
else
{
MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor);
removed = store.remove(item.mBase, count, mActor);
removed = store.remove(item.mBase, count);
}
std::stringstream error;

View file

@ -322,7 +322,7 @@ namespace MWGui
if (item.mType == ItemStack::Type_Equipped)
{
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::Ptr newStack = *invStore.unequipItemQuantity(item.mBase, mPtr, count);
MWWorld::Ptr newStack = *invStore.unequipItemQuantity(item.mBase, count);
// The unequipped item was re-stacked. We have to update the index
// since the item pointed does not exist anymore.
@ -743,7 +743,7 @@ namespace MWGui
// add to player inventory
// can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object
MWWorld::Ptr newObject
= *player.getClass().getContainerStore(player).add(object, object.getRefData().getCount(), player);
= *player.getClass().getContainerStore(player).add(object, object.getRefData().getCount());
// remove from world
MWBase::Environment::get().getWorld()->deleteObject(object);

View file

@ -136,7 +136,7 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Repair"));
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
// add gold to NPC trading gold pool
MWMechanics::CreatureStats& actorStats = mActor.getClass().getCreatureStats(mActor);

View file

@ -444,7 +444,7 @@ namespace MWGui
}
else if (key->type == Type_HandToHand)
{
store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player);
store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight);
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState::Weapon);
}
}

View file

@ -152,7 +152,7 @@ namespace MWGui
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
spells.add(mSpellsWidgetMap.find(_sender)->second);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
// add gold to NPC trading gold pool
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);

View file

@ -408,7 +408,7 @@ namespace MWGui
mSpell.mName = mNameEdit->getCaption();
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
// add gold to NPC trading gold pool
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);

View file

@ -259,11 +259,11 @@ namespace MWGui
if (amount > 0)
{
store.add(MWWorld::ContainerStore::sGoldId, amount, actor);
store.add(MWWorld::ContainerStore::sGoldId, amount);
}
else
{
store.remove(MWWorld::ContainerStore::sGoldId, -amount, actor);
store.remove(MWWorld::ContainerStore::sGoldId, -amount);
}
}

View file

@ -176,7 +176,7 @@ namespace MWGui
pcStats.increaseSkill(skillId, *class_, true);
// remove gold
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
// add gold to NPC trading gold pool
MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats(mPtr);

View file

@ -166,7 +166,7 @@ namespace MWGui
// Interior cell -> mages guild transport
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("mysticism cast"));
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
// add gold to NPC trading gold pool
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);

View file

@ -75,6 +75,8 @@ namespace MWLua
const CellT& cell, sol::optional<sol::table> type) {
ObjectIdList res = std::make_shared<std::vector<ObjectId>>();
auto visitor = [&](const MWWorld::Ptr& ptr) {
if (ptr.getRefData().isDeleted())
return true;
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
if (getLiveCellRefType(ptr.mRef) == ptr.getType())
res->push_back(getId(ptr));

View file

@ -23,6 +23,7 @@ namespace MWLua
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; }
const MWWorld::Ptr& getPtr() const { return mData.ptr(); }
struct SelfObject : public LObject
{

View file

@ -7,7 +7,10 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/scene.hpp"
#include "../mwworld/store.hpp"
#include "eventqueue.hpp"
@ -48,7 +51,7 @@ namespace MWLua
{
auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 32;
api["API_REVISION"] = 33;
api["quit"] = [lua]() {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit();
@ -83,7 +86,17 @@ namespace MWLua
api["getExteriorCell"]
= [](int x, int y) { return GCell{ MWBase::Environment::get().getWorldModel()->getExterior(x, y) }; };
api["activeActors"] = GObjectList{ worldView->getActorsInScene() };
// TODO: add world.placeNewObject(recordId, cell, pos, [rot])
api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject {
// Doesn't matter which cell to use because the new object will be in disabled state.
MWWorld::CellStore* cell = MWBase::Environment::get().getWorldScene()->getCurrentCell();
MWWorld::ManualRef mref(
MWBase::Environment::get().getWorld()->getStore(), ESM::RefId::stringRefId(recordId));
const MWWorld::Ptr& ptr = mref.getPtr();
ptr.getRefData().disable();
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cell, count.value_or(1));
return GObject(getId(newPtr));
};
return LuaUtil::makeReadOnly(api);
}

View file

@ -154,6 +154,9 @@ namespace MWLua
mWorldView.update();
std::erase_if(mActiveLocalScripts,
[](const LocalScripts* l) { return l->getPtr().isEmpty() || l->getPtr().getRefData().isDeleted(); });
mGlobalScripts.statsNextFrame();
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->statsNextFrame();

View file

@ -20,6 +20,8 @@ namespace MWLua
std::string ptrToString(const MWWorld::Ptr& ptr)
{
std::string res = "object";
if (ptr.getRefData().isDeleted())
res = "deleted object";
res.append(idToString(getId(ptr)));
res.append(" (");
res.append(getLuaObjectTypeName(ptr));

View file

@ -7,6 +7,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/scene.hpp"
#include "../mwmechanics/creaturestats.hpp"
@ -87,6 +88,8 @@ namespace MWLua
{
MWWorld::Ptr newObj = world->moveObject(obj, cell, mPos);
world->rotateObject(newObj, mRot);
if (!newObj.getRefData().isEnabled())
world->enable(newObj);
}
}
@ -198,6 +201,32 @@ namespace MWLua
context.mLuaManager->addAction(std::make_unique<ActivateAction>(context.mLua, o.id(), actor.id()));
};
auto isEnabled = [](const ObjectT& o) { return o.ptr().getRefData().isEnabled(); };
auto setEnabled = [context](const GObject& object, bool enable) {
if (enable && object.ptr().getRefData().isDeleted())
throw std::runtime_error("Object is removed");
context.mLuaManager->addAction([object, enable] {
if (object.ptr().isInCell())
{
if (enable)
MWBase::Environment::get().getWorld()->enable(object.ptr());
else
MWBase::Environment::get().getWorld()->disable(object.ptr());
}
else
{
if (enable)
object.ptr().getRefData().enable();
else
throw std::runtime_error("Objects in containers can't be disabled");
}
});
};
if constexpr (std::is_same_v<ObjectT, GObject>)
objectT["enabled"] = sol::property(isEnabled, setEnabled);
else
objectT["enabled"] = sol::readonly_property(isEnabled);
if constexpr (std::is_same_v<ObjectT, GObject>)
{ // Only for global scripts
objectT["addScript"] = [context](const GObject& object, std::string_view path, sol::object initData) {
@ -243,11 +272,74 @@ namespace MWLua
localScripts->removeScript(*scriptId);
};
objectT["teleport"] = [context](const GObject& object, std::string_view cell, const osg::Vec3f& pos,
const sol::optional<osg::Vec3f>& optRot) {
auto removeFn = [context](const GObject& object, int countToRemove) {
MWWorld::Ptr ptr = object.ptr();
int currentCount = ptr.getRefData().getCount();
if (countToRemove <= 0 || countToRemove > currentCount)
throw std::runtime_error("Can't remove " + std::to_string(countToRemove) + " of "
+ std::to_string(currentCount) + " items");
ptr.getRefData().setCount(currentCount - countToRemove); // Immediately change count
if (ptr.getContainerStore() || currentCount == countToRemove)
{
// Delayed action to trigger side effects
context.mLuaManager->addAction([object, countToRemove] {
MWWorld::Ptr ptr = object.ptr();
// Restore original count
ptr.getRefData().setCount(ptr.getRefData().getCount() + countToRemove);
// And now remove properly
if (ptr.getContainerStore())
ptr.getContainerStore()->remove(ptr, countToRemove);
else
{
MWBase::Environment::get().getWorld()->disable(object.ptr());
MWBase::Environment::get().getWorld()->deleteObject(ptr);
}
});
}
};
objectT["remove"] = [removeFn](const GObject& object, sol::optional<int> count) {
removeFn(object, count.value_or(object.ptr().getRefData().getCount()));
};
objectT["split"] = [removeFn](const GObject& object, int count) -> GObject {
removeFn(object, count);
// Doesn't matter which cell to use because the new instance will be in disabled state.
MWWorld::CellStore* cell = MWBase::Environment::get().getWorldScene()->getCurrentCell();
const MWWorld::Ptr& ptr = object.ptr();
MWWorld::Ptr splitted = ptr.getClass().copyToCell(ptr, *cell, count);
splitted.getRefData().disable();
return GObject(getId(splitted));
};
objectT["moveInto"] = [removeFn, context](const GObject& object, const Inventory<GObject>& inventory) {
// Currently moving to or from containers makes a copy and removes the original.
// TODO(#6148): actually move rather than copy and preserve RefNum
int count = object.ptr().getRefData().getCount();
removeFn(object, count);
context.mLuaManager->addAction([item = object, count, cont = inventory.mObj] {
auto& refData = item.ptr().getRefData();
refData.setCount(count); // temporarily undo removal to run ContainerStore::add
cont.ptr().getClass().getContainerStore(cont.ptr()).add(item.ptr(), count, false);
refData.setCount(0);
});
};
objectT["teleport"] = [removeFn, context](const GObject& object, std::string_view cell,
const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot) {
MWWorld::Ptr ptr = object.ptr();
if (ptr.getRefData().isDeleted())
throw std::runtime_error("Object is removed");
if (ptr.getContainerStore())
{
// Currently moving to or from containers makes a copy and removes the original.
// TODO(#6148): actually move rather than copy and preserve RefNum
auto* cellStore = MWBase::Environment::get().getWorldModel()->getCellByPosition(pos, cell);
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cellStore, ptr.getRefData().getCount());
newPtr.getRefData().disable();
removeFn(object, ptr.getRefData().getCount());
ptr = newPtr;
}
osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3();
auto action = std::make_unique<TeleportAction>(context.mLua, object.id(), cell, pos, rot);
auto action = std::make_unique<TeleportAction>(context.mLua, getId(ptr), cell, pos, rot);
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
context.mLuaManager->addTeleportPlayerAction(std::move(action));
else
@ -335,24 +427,40 @@ namespace MWLua
return ObjectList<ObjectT>{ list };
};
inventoryT["countOf"] = [](const InventoryT& inventory, const std::string& recordId) {
inventoryT["countOf"] = [](const InventoryT& inventory, std::string_view recordId) {
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
return store.count(ESM::RefId::stringRefId(recordId));
};
if constexpr (std::is_same_v<ObjectT, GObject>)
{ // Only for global scripts
// TODO
// obj.inventory:drop(obj2, [count])
// obj.inventory:drop(recordId, [count])
// obj.inventory:addNew(recordId, [count])
// obj.inventory:remove(obj/recordId, [count])
/*objectT["moveInto"] = [](const GObject& obj, const InventoryT& inventory) {};
inventoryT["drop"] = [](const InventoryT& inventory) {};
inventoryT["addNew"] = [](const InventoryT& inventory) {};
inventoryT["remove"] = [](const InventoryT& inventory) {};*/
}
inventoryT["find"] = [](const InventoryT& inventory, std::string_view recordId) -> sol::optional<ObjectT> {
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
auto itemId = ESM::RefId::stringRefId(recordId);
for (const MWWorld::Ptr& item : store)
{
if (item.getCellRef().getRefId() == itemId)
{
MWBase::Environment::get().getWorldModel()->registerPtr(item);
return ObjectT(getId(item));
}
}
return sol::nullopt;
};
inventoryT["findAll"] = [](const InventoryT& inventory, std::string_view recordId) {
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
auto itemId = ESM::RefId::stringRefId(recordId);
ObjectIdList list = std::make_shared<std::vector<ObjectId>>();
for (const MWWorld::Ptr& item : store)
{
if (item.getCellRef().getRefId() == itemId)
{
MWBase::Environment::get().getWorldModel()->registerPtr(item);
list->push_back(getId(item));
}
}
return ObjectList<ObjectT>{ list };
};
}
template <class ObjectT>

View file

@ -39,7 +39,7 @@ namespace MWLua
std::fill(usedSlots.begin(), usedSlots.end(), false);
static constexpr int anySlot = -1;
auto tryEquipToSlot = [&actor, &store, &usedSlots](int slot, const Item& item) -> bool {
auto tryEquipToSlot = [&store, &usedSlots](int slot, const Item& item) -> bool {
auto old_it = slot != anySlot ? store.getSlot(slot) : store.end();
MWWorld::Ptr itemPtr;
if (std::holds_alternative<ObjectId>(item))
@ -88,7 +88,7 @@ namespace MWLua
if (it == store.end()) // should never happen
throw std::logic_error("Item not found in container");
store.equip(slot, it, actor);
store.equip(slot, it);
return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was
// changed
};
@ -100,7 +100,7 @@ namespace MWLua
if (new_it == mEquipment.end())
{
if (old_it != store.end())
store.unequipSlot(slot, actor);
store.unequipSlot(slot);
continue;
}
if (tryEquipToSlot(slot, new_it->second))

View file

@ -199,7 +199,7 @@ namespace
continue;
// Set the soul on just one of the gems, not the whole stack
gem->getContainerStore()->unstack(*gem, caster);
gem->getContainerStore()->unstack(*gem);
gem->getCellRef().setSoul(creature.getCellRef().getRefId());
// Restack the gem with other gems with the same soul
@ -1021,14 +1021,14 @@ namespace MWMechanics
{
// For non-hostile NPCs, unequip whatever is in the left slot in favor of a light.
if (heldIter != inventoryStore.end() && heldIter->getType() != ESM::Light::sRecordId)
inventoryStore.unequipItem(*heldIter, ptr);
inventoryStore.unequipItem(*heldIter);
}
else if (heldIter == inventoryStore.end() || heldIter->getType() == ESM::Light::sRecordId)
{
// For hostile NPCs, see if they have anything better to equip first
auto shield = inventoryStore.getPreferredShield(ptr);
auto shield = inventoryStore.getPreferredShield();
if (shield != inventoryStore.end())
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, shield, ptr);
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, shield);
}
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
@ -1036,7 +1036,7 @@ namespace MWMechanics
// If we have a torch and can equip it, then equip it now.
if (heldIter == inventoryStore.end())
{
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torchIter, ptr);
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torchIter);
}
}
}
@ -1046,7 +1046,7 @@ namespace MWMechanics
{
// At day, unequip lights and auto equip shields or other suitable items
// (Note: autoEquip will ignore lights)
inventoryStore.autoEquip(ptr);
inventoryStore.autoEquip();
}
}
}
@ -1069,7 +1069,7 @@ namespace MWMechanics
timeRemaining -= duration;
if (timeRemaining <= 0.f)
{
inventoryStore.remove(*heldIter, 1, ptr); // remove it
inventoryStore.remove(*heldIter, 1); // remove it
return;
}
@ -1080,7 +1080,7 @@ namespace MWMechanics
// Both NPC and player lights extinguish in water.
if (world->isSwimming(ptr))
{
inventoryStore.remove(*heldIter, 1, ptr); // remove it
inventoryStore.remove(*heldIter, 1); // remove it
// ...But, only the player makes a sound.
if (isPlayer)

View file

@ -106,8 +106,7 @@ namespace MWMechanics
if (actor.getClass().hasInventoryStore(actor))
{
if (mWeapon.isEmpty())
actor.getClass().getInventoryStore(actor).unequipSlot(
MWWorld::InventoryStore::Slot_CarriedRight, actor);
actor.getClass().getInventoryStore(actor).unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight);
else
{
MWWorld::ActionEquip equip(mWeapon);

View file

@ -275,7 +275,7 @@ void MWMechanics::Alchemy::removeIngredients()
for (TIngredientsContainer::iterator iter(mIngredients.begin()); iter != mIngredients.end(); ++iter)
if (!iter->isEmpty())
{
iter->getContainerStore()->remove(*iter, 1, mAlchemist);
iter->getContainerStore()->remove(*iter, 1);
if (iter->getRefData().getCount() < 1)
*iter = MWWorld::Ptr();
@ -317,7 +317,7 @@ void MWMechanics::Alchemy::addPotion(const std::string& name)
if (!record)
record = MWBase::Environment::get().getWorld()->createRecord(newRecord);
mAlchemist.getClass().getContainerStore(mAlchemist).add(record->mId, 1, mAlchemist);
mAlchemist.getClass().getContainerStore(mAlchemist).add(record->mId, 1);
}
void MWMechanics::Alchemy::increaseSkill()

View file

@ -140,7 +140,7 @@ namespace MWMechanics
shieldhealth -= std::min(shieldhealth, int(damage));
shield->getCellRef().setCharge(shieldhealth);
if (shieldhealth == 0)
inv.unequipItem(*shield, blocker);
inv.unequipItem(*shield);
// Reduce blocker fatigue
static const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->mValue.getFloat();
static const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->mValue.getFloat();
@ -285,7 +285,7 @@ namespace MWMechanics
static const float fProjectileThrownStoreChance
= gmst.find("fProjectileThrownStoreChance")->mValue.getFloat();
if (Misc::Rng::rollProbability(world->getPrng()) < fProjectileThrownStoreChance / 100.f)
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
victim.getClass().getContainerStore(victim).add(projectile, 1);
}
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true);
@ -422,7 +422,7 @@ namespace MWMechanics
// Weapon broken? unequip it
if (weaphealth == 0)
weapon = *attacker.getClass().getInventoryStore(attacker).unequipItem(weapon, attacker);
weapon = *attacker.getClass().getInventoryStore(attacker).unequipItem(weapon);
}
}

View file

@ -70,12 +70,12 @@ namespace MWMechanics
enchantment.mData.mType = mCastStyle;
enchantment.mData.mCost = getBaseCastCost();
store.remove(mSoulGemPtr, 1, player);
store.remove(mSoulGemPtr, 1);
// Exception for Azura Star, new one will be added after enchanting
auto azurasStarId = ESM::RefId::stringRefId("Misc_SoulGem_Azura");
if (mSoulGemPtr.get<ESM::Miscellaneous>()->mBase->mId == azurasStarId)
store.add(azurasStarId, 1, player);
store.add(azurasStarId, 1);
if (mSelfEnchanting)
{
@ -105,8 +105,8 @@ namespace MWMechanics
= mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
// Add the new item to player inventory and remove the old one
store.remove(mOldItemPtr, count, player);
store.add(newItemId, count, player);
store.remove(mOldItemPtr, count);
store.add(newItemId, count);
if (!mSelfEnchanting)
payForEnchantment();
@ -399,7 +399,7 @@ namespace MWMechanics
const MWWorld::Ptr& player = getPlayer();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player);
store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice());
// add gold to NPC trading gold pool
CreatureStats& enchanterStats = mEnchanter.getClass().getCreatureStats(mEnchanter);

View file

@ -267,8 +267,8 @@ namespace MWMechanics
// equippable
MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr);
for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i)
invStore.unequipAll(ptr);
invStore.autoEquip(ptr);
invStore.unequipAll();
invStore.autoEquip();
}
MechanicsManager::MechanicsManager()
@ -1033,8 +1033,8 @@ namespace MWMechanics
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
// move items from player to owner and report about theft
victim.getClass().getContainerStore(victim).add(item, toRemove, victim);
store.remove(item, toRemove, player);
victim.getClass().getContainerStore(victim).add(item, toRemove);
store.remove(item, toRemove);
commitCrime(
player, victim, OT_Theft, item.getCellRef().getFaction(), item.getClass().getValue(item) * toRemove);
}
@ -1063,8 +1063,8 @@ namespace MWMechanics
int toMove = it->getRefData().getCount() - itemCount;
containerStore.add(*it, toMove, targetContainer);
store.remove(*it, toMove, player);
containerStore.add(*it, toMove);
store.remove(*it, toMove);
}
// TODO: unhardcode the locklevel
targetContainer.getCellRef().lock(50);
@ -1836,14 +1836,14 @@ namespace MWMechanics
if (werewolf)
{
inv.unequipAll(actor);
inv.unequipAll();
inv.equip(MWWorld::InventoryStore::Slot_Robe,
inv.ContainerStore::add(ESM::RefId::stringRefId("werewolfrobe"), 1, actor), actor);
inv.ContainerStore::add(ESM::RefId::stringRefId("werewolfrobe"), 1));
}
else
{
inv.unequipSlot(MWWorld::InventoryStore::Slot_Robe, actor);
inv.ContainerStore::remove(ESM::RefId::stringRefId("werewolfrobe"), 1, actor);
inv.unequipSlot(MWWorld::InventoryStore::Slot_Robe);
inv.ContainerStore::remove(ESM::RefId::stringRefId("werewolfrobe"), 1);
}
if (actor == player->getPlayer())

View file

@ -83,7 +83,7 @@ namespace MWMechanics
}
player.getClass().skillUsageSucceeded(player, ESM::Skill::Enchant, 0);
gem.getContainerStore()->remove(gem, 1, player);
gem.getContainerStore()->remove(gem, 1);
if (gem.getRefData().getCount() == 0)
{
@ -100,7 +100,7 @@ namespace MWMechanics
const ESM::RefId soulGemAzura = ESM::RefId::stringRefId("Misc_SoulGem_Azura");
// special case: readd Azura's Star
if (gem.get<ESM::Miscellaneous>()->mBase->mId == soulGemAzura)
player.getClass().getContainerStore(player).add(soulGemAzura, 1, player);
player.getClass().getContainerStore(player).add(soulGemAzura, 1);
}
return true;

View file

@ -23,7 +23,7 @@ namespace MWMechanics
MWWorld::LiveCellRef<ESM::Repair>* ref = mTool.get<ESM::Repair>();
// unstack tool if required
player.getClass().getContainerStore(player).unstack(mTool, player);
player.getClass().getContainerStore(player).unstack(mTool);
// reduce number of uses left
int uses = mTool.getClass().getItemHealth(mTool);
@ -85,7 +85,7 @@ namespace MWMechanics
{
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
store.remove(mTool, 1, player);
store.remove(mTool, 1);
std::string message = MWBase::Environment::get()
.getWorld()

View file

@ -72,7 +72,7 @@ namespace MWMechanics
lockpick.getCellRef().setCharge(--uses);
if (!uses)
lockpick.getContainerStore()->remove(lockpick, 1, mActor);
lockpick.getContainerStore()->remove(lockpick, 1);
}
void Security::probeTrap(const MWWorld::Ptr& trap, const MWWorld::Ptr& probe, std::string_view& resultMessage,
@ -124,7 +124,7 @@ namespace MWMechanics
probe.getCellRef().setCharge(--uses);
if (!uses)
probe.getContainerStore()->remove(probe, 1, mActor);
probe.getContainerStore()->remove(probe, 1);
}
}

View file

@ -360,7 +360,7 @@ namespace MWMechanics
else if (type == ESM::Enchantment::CastOnce)
{
if (!godmode)
item.getContainerStore()->remove(item, 1, mCaster);
item.getContainerStore()->remove(item, 1);
}
else if (type == ESM::Enchantment::WhenStrikes)
{

View file

@ -151,9 +151,9 @@ namespace
{
// Will unequip the broken item and try to find a replacement
if (ptr != MWMechanics::getPlayer())
inv.autoEquip(ptr);
inv.autoEquip();
else
inv.unequipItem(*item, ptr);
inv.unequipItem(*item);
}
return true;
@ -173,7 +173,7 @@ namespace
void addBoundItem(const ESM::RefId& itemId, const MWWorld::Ptr& actor)
{
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
MWWorld::Ptr boundPtr = *store.MWWorld::ContainerStore::add(itemId, 1, actor);
MWWorld::Ptr boundPtr = *store.MWWorld::ContainerStore::add(itemId, 1);
int slot = getBoundItemSlot(boundPtr);
auto prevItem = slot >= 0 ? store.getSlot(slot) : store.end();
@ -217,9 +217,9 @@ namespace
bool wasEquipped = currentItem != store.end() && currentItem->getCellRef().getRefId() == itemId;
if (wasEquipped)
store.remove(*currentItem, 1, actor);
store.remove(*currentItem, 1);
else
store.remove(itemId, 1, actor);
store.remove(itemId, 1);
if (actor != MWMechanics::getPlayer())
{
@ -240,7 +240,7 @@ namespace
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
return;
actor.getClass().getInventoryStore(actor).autoEquip(actor);
actor.getClass().getInventoryStore(actor).autoEquip();
return;
}
@ -517,7 +517,7 @@ namespace MWMechanics
if (target.getClass().hasInventoryStore(target))
{
auto& store = target.getClass().getInventoryStore(target);
store.unequipAll(target);
store.unequipAll();
}
else
invalid = true;
@ -1072,7 +1072,7 @@ namespace MWMechanics
break;
case ESM::MagicEffect::ExtraSpell:
if (magnitudes.get(effect.mEffectId).getMagnitude() <= 0.f)
target.getClass().getInventoryStore(target).autoEquip(target);
target.getClass().getInventoryStore(target).autoEquip();
break;
case ESM::MagicEffect::TurnUndead:
{

View file

@ -52,7 +52,7 @@ namespace MWRender
if (animated)
addAnimSource(model, model);
mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr);
mPtr.getClass().getInventoryStore(mPtr).setInvListener(this);
updateParts();
}

View file

@ -120,7 +120,7 @@ namespace MWRender
if (mObjects.emplace(ptr.mRef, anim).second)
{
ptr.getClass().getInventoryStore(ptr).setInvListener(anim.get(), ptr);
ptr.getClass().getInventoryStore(ptr).setInvListener(anim.get());
ptr.getClass().getInventoryStore(ptr).setContListener(anim.get());
}
}
@ -140,7 +140,7 @@ namespace MWRender
if (ptr.getClass().isActor())
{
if (ptr.getClass().hasInventoryStore(ptr))
ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr, ptr);
ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr);
ptr.getClass().getContainerStore(ptr).setContListener(nullptr);
}
@ -163,7 +163,7 @@ namespace MWRender
if (ptr.getClass().isActor() && ptr.getRefData().getCustomData())
{
if (ptr.getClass().hasInventoryStore(ptr))
ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr, ptr);
ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr);
ptr.getClass().getContainerStore(ptr).setContListener(nullptr);
}

View file

@ -140,7 +140,7 @@ namespace MWRender
showWeapon(false);
inv.remove(*weapon, 1, actor);
inv.remove(*weapon, 1);
}
else
{
@ -167,7 +167,7 @@ namespace MWRender
MWBase::Environment::get().getWorld()->launchProjectile(
actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength);
inv.remove(ammoPtr, 1, actor);
inv.remove(ammoPtr, 1);
mAmmunition.reset();
}
}

View file

@ -35,23 +35,21 @@
namespace
{
void addToStore(
const MWWorld::Ptr& itemPtr, int count, MWWorld::Ptr& ptr, MWWorld::ContainerStore& store, bool resolve = true)
void addToStore(const MWWorld::Ptr& itemPtr, int count, MWWorld::ContainerStore& store, bool resolve = true)
{
if (itemPtr.getClass().getScript(itemPtr).empty())
{
store.add(itemPtr, count, ptr, true, resolve);
store.add(itemPtr, count, 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);
store.add(itemPtr, 1, true, resolve);
}
}
void addRandomToStore(const MWWorld::Ptr& itemPtr, int count, MWWorld::Ptr& owner, MWWorld::ContainerStore& store,
bool topLevel = true)
void addRandomToStore(const MWWorld::Ptr& itemPtr, int count, MWWorld::ContainerStore& store, bool topLevel = true)
{
if (itemPtr.getType() == ESM::ItemLevList::sRecordId)
{
@ -60,7 +58,7 @@ namespace
if (topLevel && count > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
{
for (int i = 0; i < count; i++)
addRandomToStore(itemPtr, 1, owner, store, true);
addRandomToStore(itemPtr, 1, store, true);
}
else
{
@ -70,11 +68,11 @@ namespace
if (itemId.empty())
return;
MWWorld::ManualRef manualRef(MWBase::Environment::get().getWorld()->getStore(), itemId, 1);
addRandomToStore(manualRef.getPtr(), count, owner, store, false);
addRandomToStore(manualRef.getPtr(), count, store, false);
}
}
else
addToStore(itemPtr, count, owner, store);
addToStore(itemPtr, count, store);
}
}
@ -141,20 +139,20 @@ namespace MWScript
{
if (store.isResolved())
{
addRandomToStore(itemPtr, count, ptr, store);
addRandomToStore(itemPtr, count, store);
}
}
else
addToStore(itemPtr, count, ptr, store, store.isResolved());
addToStore(itemPtr, count, store, store.isResolved());
}
}
return;
}
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
if (isLevelledList)
addRandomToStore(itemPtr, count, ptr, store);
addRandomToStore(itemPtr, count, store);
else
addToStore(itemPtr, count, ptr, store);
addToStore(itemPtr, count, 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())
@ -246,7 +244,7 @@ namespace MWScript
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, false);
store.remove(item, count, false, false);
}
}
return;
@ -263,7 +261,7 @@ namespace MWScript
}
}
int numRemoved = store.remove(item, count, ptr);
int numRemoved = store.remove(item, count);
// Spawn a messagebox (only for items removed from player's inventory)
if ((numRemoved > 0) && (ptr == MWMechanics::getPlayer()))
@ -318,7 +316,7 @@ namespace MWScript
if (found == invStore.end())
{
MWWorld::ManualRef ref(store, item, 1);
found = ptr.getClass().getContainerStore(ptr).add(ref.getPtr(), 1, ptr, false);
found = ptr.getClass().getContainerStore(ptr).add(ref.getPtr(), 1, false);
Log(Debug::Warning) << "Implicitly adding one " << item << " to the inventory store of "
<< ptr.getCellRef().getRefId()
<< " to fulfill the requirements of Equip instruction";

View file

@ -580,10 +580,10 @@ namespace MWScript
store.get<ESM::Creature>().find(
creature); // This line throws an exception if it can't find the creature
MWWorld::Ptr item = *ptr.getClass().getContainerStore(ptr).add(gem, 1, ptr);
MWWorld::Ptr item = *ptr.getClass().getContainerStore(ptr).add(gem, 1);
// Set the soul on just one of the gems, not the whole stack
item.getContainerStore()->unstack(item, ptr);
item.getContainerStore()->unstack(item);
item.getCellRef().setSoul(creature);
// Restack the gem with other gems with the same soul
@ -614,7 +614,7 @@ namespace MWScript
{
if (it->getCellRef().getSoul() == soul)
{
store.remove(*it, 1, ptr);
store.remove(*it, 1);
return;
}
}
@ -667,7 +667,7 @@ namespace MWScript
if (it != store.end() && it->getCellRef().getRefId() == item)
{
int numToRemove = std::min(amount - numNotEquipped, it->getRefData().getCount());
store.unequipItemQuantity(*it, ptr, numToRemove);
store.unequipItemQuantity(*it, numToRemove);
numNotEquipped += numToRemove;
}
}
@ -676,7 +676,7 @@ namespace MWScript
{
if (iter->getCellRef().getRefId() == item && !store.isEquipped(*iter))
{
int removed = store.remove(*iter, amount, ptr);
int removed = store.remove(*iter, amount);
MWWorld::Ptr dropped
= MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed);
dropped.getCellRef().setOwner(ESM::RefId::sEmpty);
@ -732,7 +732,7 @@ namespace MWScript
if (iter->getCellRef().getSoul() == soul)
{
MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, 1);
store.remove(*iter, 1, ptr);
store.remove(*iter, 1);
break;
}
}

View file

@ -75,7 +75,7 @@ namespace MWWorld
if (invStore.getSlot(*slot) == invStore.end())
{
// slot is not occupied
invStore.equip(*slot, it, actor);
invStore.equip(*slot, it);
break;
}
}
@ -88,14 +88,14 @@ namespace MWWorld
bool reEquip = false;
for (slot = slots_.first.begin(); slot != slots_.first.end(); ++slot)
{
invStore.unequipSlot(*slot, actor, false);
invStore.unequipSlot(*slot, false);
if (slot + 1 != slots_.first.end())
{
invStore.equip(*slot, invStore.getSlot(*(slot + 1)), actor);
invStore.equip(*slot, invStore.getSlot(*(slot + 1)));
}
else
{
invStore.equip(*slot, it, actor);
invStore.equip(*slot, it);
}
// Fix for issue of selected enchated item getting remmoved on cycle

View file

@ -42,8 +42,8 @@ namespace MWWorld
// not work for a last item in the container - empty harvested containers are considered as "allowed to
// use".
MWBase::Environment::get().getMechanicsManager()->itemTaken(actor, *it, target, itemCount);
actorStore.add(*it, itemCount, actor);
store.remove(*it, itemCount, getTarget());
actorStore.add(*it, itemCount);
store.remove(*it, itemCount);
std::string name{ it->getClass().getName(*it) };
takenMap[name] += itemCount;
}

View file

@ -33,7 +33,7 @@ namespace MWWorld
MWBase::Environment::get().getMechanicsManager()->itemTaken(
actor, getTarget(), MWWorld::Ptr(), getTarget().getRefData().getCount());
MWWorld::Ptr newitem
= *actor.getClass().getContainerStore(actor).add(getTarget(), getTarget().getRefData().getCount(), actor);
= *actor.getClass().getContainerStore(actor).add(getTarget(), getTarget().getRefData().getCount());
MWBase::Environment::get().getWorld()->deleteObject(getTarget());
setTarget(newitem);
}

View file

@ -52,6 +52,7 @@
#include "class.hpp"
#include "containerstore.hpp"
#include "esmstore.hpp"
#include "inventorystore.hpp"
#include "ptr.hpp"
#include "worldmodel.hpp"
@ -264,10 +265,9 @@ namespace
if (!iter->mData.isEnabled())
{
iter->mData.enable();
MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore));
MWBase::Environment::get().getWorld()->disable(ptr);
}
else
MWBase::Environment::get().getWorldModel()->registerPtr(MWWorld::Ptr(&*iter, cellstore));
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
return;
}
@ -444,7 +444,11 @@ namespace MWWorld
mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo));
updateMergedRefs();
return MWWorld::Ptr(object.getBase(), cellToMoveTo);
MWWorld::Ptr ptr(object.getBase(), cellToMoveTo);
const Class& cls = ptr.getClass();
if (cls.hasInventoryStore(ptr))
cls.getInventoryStore(ptr).setActor(ptr);
return ptr;
}
struct MergeVisitor

View file

@ -15,8 +15,10 @@
#include "actiontake.hpp"
#include "containerstore.hpp"
#include "failedaction.hpp"
#include "inventorystore.hpp"
#include "nullaction.hpp"
#include "ptr.hpp"
#include "worldmodel.hpp"
#include "../mwgui/tooltips.hpp"
@ -372,6 +374,9 @@ namespace MWWorld
Ptr newPtr = copyToCellImpl(ptr, cell);
newPtr.getCellRef().unsetRefNum(); // This RefNum is only valid within the original cell of the reference
newPtr.getRefData().setCount(count);
MWBase::Environment::get().getWorldModel()->registerPtr(newPtr);
if (hasInventoryStore(newPtr))
getInventoryStore(newPtr).setActor(newPtr);
return newPtr;
}

View file

@ -192,6 +192,12 @@ int MWWorld::ContainerStore::count(const ESM::RefId& id) const
return total;
}
void MWWorld::ContainerStore::clearRefNums()
{
for (const auto& iter : *this)
iter.getCellRef().unsetRefNum();
}
MWWorld::ContainerStoreListener* MWWorld::ContainerStore::getContListener() const
{
return mListener;
@ -202,7 +208,7 @@ void MWWorld::ContainerStore::setContListener(MWWorld::ContainerStoreListener* l
mListener = listener;
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr& ptr, const Ptr& container, int count)
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr& ptr, int count)
{
resolve();
if (ptr.getRefData().getCount() <= count)
@ -212,7 +218,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr& ptr,
if (!script.empty())
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it);
remove(ptr, ptr.getRefData().getCount() - count, container);
remove(ptr, ptr.getRefData().getCount() - count);
return it;
}
@ -285,14 +291,14 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
&& cls2.getItemHealth(ptr2) == cls2.getItemMaxHealth(ptr2)));
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const ESM::RefId& id, int count, const Ptr& actorPtr)
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const ESM::RefId& id, int count)
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count);
return add(ref.getPtr(), count, actorPtr);
return add(ref.getPtr(), count);
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(
const Ptr& itemPtr, int count, const Ptr& actorPtr, bool /*allowAutoEquip*/, bool resolve)
const Ptr& itemPtr, int count, bool /*allowAutoEquip*/, bool resolve)
{
Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -326,7 +332,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(
const ESM::RefId& script = item.getClass().getScript(item);
if (!script.empty())
{
if (actorPtr == player)
if (mActor == player)
{
// Items in player's inventory have cell set to 0, so their scripts will never be removed
item.mCell = nullptr;
@ -335,7 +341,10 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(
{
// Set mCell to the cell of the container/actor, so that the scripts are removed properly when
// the cell of the container/actor goes inactive
item.mCell = actorPtr.getCell();
if (!mPtr.isEmpty())
item.mCell = mPtr.getCell();
else if (!mActor.isEmpty())
item.mCell = mActor.getCell();
}
item.mContainerStore = this;
@ -344,12 +353,12 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(
// Set OnPCAdd special variable, if it is declared
// Make sure to do this *after* we have added the script to LocalScripts
if (actorPtr == player)
if (mActor == player)
item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1);
}
// we should not fire event for InventoryStore yet - it has some custom logic
if (mListener && !actorPtr.getClass().hasInventoryStore(actorPtr))
if (mListener && !(!mActor.isEmpty() && mActor.getClass().hasInventoryStore(mActor)))
mListener->itemAdded(item, count);
return it;
@ -504,8 +513,7 @@ void MWWorld::ContainerStore::updateRechargingItems()
}
}
int MWWorld::ContainerStore::remove(
const ESM::RefId& itemId, int count, const Ptr& actor, bool equipReplacement, bool resolveFirst)
int MWWorld::ContainerStore::remove(const ESM::RefId& itemId, int count, bool equipReplacement, bool resolveFirst)
{
if (resolveFirst)
resolve();
@ -513,7 +521,7 @@ int MWWorld::ContainerStore::remove(
for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)
if (iter->getCellRef().getRefId() == itemId)
toRemove -= remove(*iter, toRemove, actor, equipReplacement, resolveFirst);
toRemove -= remove(*iter, toRemove, equipReplacement, resolveFirst);
flagAsModified();
@ -532,8 +540,7 @@ bool MWWorld::ContainerStore::hasVisibleItems() const
return false;
}
int MWWorld::ContainerStore::remove(
const Ptr& item, int count, const Ptr& actor, bool equipReplacement, bool resolveFirst)
int MWWorld::ContainerStore::remove(const Ptr& item, int count, bool equipReplacement, bool resolveFirst)
{
assert(this == item.getContainerStore());
if (resolveFirst)
@ -556,7 +563,7 @@ int MWWorld::ContainerStore::remove(
flagAsModified();
// we should not fire event for InventoryStore yet - it has some custom logic
if (mListener && !actor.getClass().hasInventoryStore(actor))
if (mListener && !(!mActor.isEmpty() && mActor.getClass().hasInventoryStore(mActor)))
mListener->itemRemoved(item, count - toRemove);
// number of removed items

View file

@ -102,12 +102,21 @@ namespace MWWorld
protected:
ContainerStoreListener* mListener;
// Used in clone() to unset refnums of copies.
// (RefNum should be unique, copy can not have the same RefNum).
void clearRefNums();
// (item, max charge)
typedef std::vector<std::pair<ContainerStoreIterator, float>> TRechargingItems;
TRechargingItems mRechargingItems;
bool mRechargingItemsUpToDate;
// Non-empty only if is InventoryStore.
// The actor whose inventory it is.
// TODO: Consider merging mActor and mPtr.
MWWorld::Ptr mActor;
private:
MWWorld::CellRefList<ESM::Potion> potions;
MWWorld::CellRefList<ESM::Apparatus> appas;
@ -128,7 +137,7 @@ namespace MWWorld
bool mModified;
bool mResolved;
unsigned int mSeed;
MWWorld::Ptr mPtr;
MWWorld::Ptr mPtr; // Container that contains this store. Set in MWClass::Container::getContainerStore
std::weak_ptr<ResolutionListener> mResolutionListener;
ContainerStoreIterator addImp(const Ptr& ptr, int count, bool markModified = true);
@ -160,7 +169,12 @@ namespace MWWorld
virtual ~ContainerStore();
virtual std::unique_ptr<ContainerStore> clone() { return std::make_unique<ContainerStore>(*this); }
virtual std::unique_ptr<ContainerStore> clone()
{
auto res = std::make_unique<ContainerStore>(*this);
res->clearRefNums();
return res;
}
ConstContainerStoreIterator cbegin(int mask = Type_All) const;
ConstContainerStoreIterator cend() const;
@ -173,7 +187,7 @@ namespace MWWorld
bool hasVisibleItems() const;
virtual ContainerStoreIterator add(
const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true);
const Ptr& itemPtr, int count, 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.
@ -184,17 +198,15 @@ namespace MWWorld
/// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to
/// the newly inserted item.
ContainerStoreIterator add(const ESM::RefId& id, int count, const Ptr& actorPtr);
ContainerStoreIterator add(const ESM::RefId& id, int count);
///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true)
int remove(
const ESM::RefId& itemId, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true);
int remove(const ESM::RefId& itemId, int count, bool equipReplacement = 0, bool resolve = true);
///< Remove \a count item(s) designated by \a itemId from this container.
///
/// @return the number of items actually removed
virtual int remove(
const Ptr& item, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true);
virtual int remove(const Ptr& item, int count, bool equipReplacement = 0, bool resolve = true);
///< Remove \a count item(s) designated by \a item from this inventory.
///
/// @return the number of items actually removed
@ -202,7 +214,7 @@ namespace MWWorld
void rechargeItems(float duration);
///< Restore charge on enchanted items. Note this should only be done for the player.
ContainerStoreIterator unstack(const Ptr& ptr, const Ptr& container, int count = 1);
ContainerStoreIterator unstack(const Ptr& ptr, int count = 1);
///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added
///< with (origCount-count).
///

View file

@ -128,18 +128,18 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator=(const InventoryStore
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(
const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip, bool resolve)
const Ptr& itemPtr, int count, bool allowAutoEquip, bool resolve)
{
const MWWorld::ContainerStoreIterator& retVal
= MWWorld::ContainerStore::add(itemPtr, count, actorPtr, allowAutoEquip, resolve);
= MWWorld::ContainerStore::add(itemPtr, count, allowAutoEquip, resolve);
// Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves
if (allowAutoEquip && actorPtr != MWMechanics::getPlayer() && actorPtr.getClass().isNpc()
&& !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())
if (allowAutoEquip && mActor != MWMechanics::getPlayer() && mActor.getClass().isNpc()
&& !mActor.getClass().getNpcStats(mActor).isWerewolf())
{
auto type = itemPtr.getType();
if (type == ESM::Armor::sRecordId || type == ESM::Clothing::sRecordId)
autoEquip(actorPtr);
autoEquip();
}
if (mListener)
@ -148,7 +148,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(
return retVal;
}
void MWWorld::InventoryStore::equip(int slot, const ContainerStoreIterator& iterator, const Ptr& actor)
void MWWorld::InventoryStore::equip(int slot, const ContainerStoreIterator& iterator)
{
if (iterator == end())
throw std::runtime_error("can't equip end() iterator, use unequip function instead");
@ -167,31 +167,31 @@ void MWWorld::InventoryStore::equip(int slot, const ContainerStoreIterator& iter
throw std::runtime_error("invalid slot");
if (mSlots[slot] != end())
unequipSlot(slot, actor);
unequipSlot(slot);
// unstack item pointed to by iterator if required
if (iterator != end() && !slots_.second
&& iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped
{
unstack(*iterator, actor);
unstack(*iterator);
}
mSlots[slot] = iterator;
flagAsModified();
fireEquipmentChangedEvent(actor);
fireEquipmentChangedEvent();
}
void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor)
void MWWorld::InventoryStore::unequipAll()
{
mUpdatesEnabled = false;
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
unequipSlot(slot, actor);
unequipSlot(slot);
mUpdatesEnabled = true;
fireEquipmentChangedEvent(actor);
fireEquipmentChangedEvent();
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot(int slot)
@ -223,14 +223,14 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot(int slot) cons
return mSlots[slot];
}
void MWWorld::InventoryStore::autoEquipWeapon(const MWWorld::Ptr& actor, TSlots& slots_)
void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
{
if (!actor.getClass().isNpc())
if (!mActor.getClass().isNpc())
{
// In original game creatures do not autoequip weapon, but we need it for weapon sheathing.
// The only case when the difference is noticable - when this creature sells weapon.
// So just disable weapon autoequipping for creatures which sells weapon.
int services = actor.getClass().getServices(actor);
int services = mActor.getClass().getServices(mActor);
bool sellsWeapon = services & (ESM::NPC::Weapon | ESM::NPC::MagicItems);
if (sellsWeapon)
return;
@ -285,7 +285,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(const MWWorld::Ptr& actor, TSlots&
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
{
float skillValue = actor.getClass().getSkill(actor, static_cast<int>(weaponSkills[j]));
float skillValue = mActor.getClass().getSkill(mActor, static_cast<int>(weaponSkills[j]));
if (skillValue > max && !weaponSkillVisited[j])
{
max = skillValue;
@ -328,7 +328,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(const MWWorld::Ptr& actor, TSlots&
}
}
if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first)
if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, mActor).first)
{
// Do not equip ranged weapons, if there is no suitable ammo
bool hasAmmo = true;
@ -360,7 +360,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(const MWWorld::Ptr& actor, TSlots&
{
if (weapon->getRefData().getCount() > 1)
{
unstack(*weapon, actor);
unstack(*weapon);
}
}
@ -379,13 +379,13 @@ void MWWorld::InventoryStore::autoEquipWeapon(const MWWorld::Ptr& actor, TSlots&
}
}
void MWWorld::InventoryStore::autoEquipArmor(const MWWorld::Ptr& actor, TSlots& slots_)
void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
{
// Only NPCs can wear armor for now.
// For creatures we equip only shields.
if (!actor.getClass().isNpc())
if (!mActor.getClass().isNpc())
{
autoEquipShield(actor, slots_);
autoEquipShield(slots_);
return;
}
@ -395,7 +395,7 @@ void MWWorld::InventoryStore::autoEquipArmor(const MWWorld::Ptr& actor, TSlots&
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored);
float unarmoredSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Unarmored);
float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter != end();
@ -403,7 +403,7 @@ void MWWorld::InventoryStore::autoEquipArmor(const MWWorld::Ptr& actor, TSlots&
{
Ptr test = *iter;
switch (test.getClass().canBeEquipped(test, actor).first)
switch (test.getClass().canBeEquipped(test, mActor).first)
{
case 0:
continue;
@ -412,7 +412,7 @@ void MWWorld::InventoryStore::autoEquipArmor(const MWWorld::Ptr& actor, TSlots&
}
if (iter.getType() == ContainerStore::Type_Armor
&& test.getClass().getEffectiveArmorRating(test, actor) <= std::max(unarmoredRating, 0.f))
&& test.getClass().getEffectiveArmorRating(test, mActor) <= std::max(unarmoredRating, 0.f))
{
continue;
}
@ -437,8 +437,8 @@ void MWWorld::InventoryStore::autoEquipArmor(const MWWorld::Ptr& actor, TSlots&
if (old.get<ESM::Armor>()->mBase->mData.mType == test.get<ESM::Armor>()->mBase->mData.mType)
{
if (old.getClass().getEffectiveArmorRating(old, actor)
>= test.getClass().getEffectiveArmorRating(test, actor))
if (old.getClass().getEffectiveArmorRating(old, mActor)
>= test.getClass().getEffectiveArmorRating(test, mActor))
// old armor had better armor rating
continue;
}
@ -483,7 +483,7 @@ void MWWorld::InventoryStore::autoEquipArmor(const MWWorld::Ptr& actor, TSlots&
// unstack item pointed to by iterator if required
if (iter->getRefData().getCount() > 1)
{
unstack(*iter, actor);
unstack(*iter);
}
}
@ -494,13 +494,13 @@ void MWWorld::InventoryStore::autoEquipArmor(const MWWorld::Ptr& actor, TSlots&
}
}
void MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_)
void MWWorld::InventoryStore::autoEquipShield(TSlots& slots_)
{
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter)
{
if (iter->get<ESM::Armor>()->mBase->mData.mType != ESM::Armor::Shield)
continue;
if (iter->getClass().canBeEquipped(*iter, actor).first != 1)
if (iter->getClass().canBeEquipped(*iter, mActor).first != 1)
continue;
std::pair<std::vector<int>, bool> shieldSlots = iter->getClass().getEquipmentSlots(*iter);
int slot = shieldSlots.first[0];
@ -515,7 +515,7 @@ void MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor, TSlots&
}
}
void MWWorld::InventoryStore::autoEquip(const MWWorld::Ptr& actor)
void MWWorld::InventoryStore::autoEquip()
{
TSlots slots_;
initSlots(slots_);
@ -527,8 +527,8 @@ void MWWorld::InventoryStore::autoEquip(const MWWorld::Ptr& actor)
// Equipping lights is handled in Actors::updateEquippedLight based on environment light.
// Note: creatures ignore equipment armor rating and only equip shields
// Use custom logic for them - select shield based on its health instead of armor rating
autoEquipWeapon(actor, slots_);
autoEquipArmor(actor, slots_);
autoEquipWeapon(slots_);
autoEquipArmor(slots_);
bool changed = false;
@ -545,16 +545,16 @@ void MWWorld::InventoryStore::autoEquip(const MWWorld::Ptr& actor)
if (changed)
{
mSlots.swap(slots_);
fireEquipmentChangedEvent(actor);
fireEquipmentChangedEvent();
flagAsModified();
}
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getPreferredShield(const MWWorld::Ptr& actor)
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getPreferredShield()
{
TSlots slots;
initSlots(slots);
autoEquipArmor(actor, slots);
autoEquipArmor(slots);
return slots[Slot_CarriedLeft];
}
@ -588,9 +588,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem(
return mSelectedEnchantItem;
}
int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement, bool resolve)
int MWWorld::InventoryStore::remove(const Ptr& item, int count, bool equipReplacement, bool resolve)
{
int retCount = ContainerStore::remove(item, count, actor, equipReplacement, resolve);
int retCount = ContainerStore::remove(item, count, equipReplacement, resolve);
bool wasEquipped = false;
if (!item.getRefData().getCount())
@ -602,7 +602,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor
if (*mSlots[slot] == item)
{
unequipSlot(slot, actor);
unequipSlot(slot);
wasEquipped = true;
break;
}
@ -612,12 +612,12 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor
// If an armor/clothing item is removed, try to find a replacement,
// but not for the player nor werewolves, and not if the RemoveItem script command
// was used (equipReplacement is false)
if (equipReplacement && wasEquipped && (actor != MWMechanics::getPlayer()) && actor.getClass().isNpc()
&& !actor.getClass().getNpcStats(actor).isWerewolf())
if (equipReplacement && wasEquipped && (mActor != MWMechanics::getPlayer()) && mActor.getClass().isNpc()
&& !mActor.getClass().getNpcStats(mActor).isWerewolf())
{
auto type = item.getType();
if (type == ESM::Armor::sRecordId || type == ESM::Clothing::sRecordId)
autoEquip(actor);
autoEquip();
}
if (item.getRefData().getCount() == 0 && mSelectedEnchantItem != end() && *mSelectedEnchantItem == item)
@ -631,8 +631,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor
return retCount;
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(
int slot, const MWWorld::Ptr& actor, bool applyUpdates)
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, bool applyUpdates)
{
if (slot < 0 || slot >= static_cast<int>(mSlots.size()))
throw std::runtime_error("slot number out of range");
@ -650,7 +649,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(
{
retval = restack(*it);
if (actor == MWMechanics::getPlayer())
if (mActor == MWMechanics::getPlayer())
{
// Unset OnPCEquip Variable on item's script, if it has a script with that variable declared
const ESM::RefId& script = it->getClass().getScript(*it);
@ -666,7 +665,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(
if (applyUpdates)
{
fireEquipmentChangedEvent(actor);
fireEquipmentChangedEvent();
}
return retval;
@ -675,21 +674,19 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(
return it;
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(
const MWWorld::Ptr& item, const MWWorld::Ptr& actor)
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWorld::Ptr& item)
{
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{
MWWorld::ContainerStoreIterator equipped = getSlot(slot);
if (equipped != end() && *equipped == item)
return unequipSlot(slot, actor);
return unequipSlot(slot);
}
throw std::runtime_error("attempt to unequip an item that is not currently equipped");
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(
const Ptr& item, const Ptr& actor, int count)
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(const Ptr& item, int count)
{
if (!isEquipped(item))
throw std::runtime_error("attempt to unequip an item that is not currently equipped");
@ -699,7 +696,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(
throw std::runtime_error("attempt to unequip more items than equipped");
if (count == item.getRefData().getCount())
return unequipItem(item, actor);
return unequipItem(item);
// Move items to an existing stack if possible, otherwise split count items out into a new stack.
// Moving counts manually here, since ContainerStore's restack can't target unequipped stacks.
@ -713,7 +710,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(
}
}
return unstack(item, actor, item.getRefData().getCount() - count);
return unstack(item, item.getRefData().getCount() - count);
}
MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getInvListener() const
@ -721,12 +718,12 @@ MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getInvListener() const
return mInventoryListener;
}
void MWWorld::InventoryStore::setInvListener(InventoryStoreListener* listener, const Ptr& actor)
void MWWorld::InventoryStore::setInvListener(InventoryStoreListener* listener)
{
mInventoryListener = listener;
}
void MWWorld::InventoryStore::fireEquipmentChangedEvent(const Ptr& actor)
void MWWorld::InventoryStore::fireEquipmentChangedEvent()
{
if (!mUpdatesEnabled)
return;
@ -735,7 +732,7 @@ void MWWorld::InventoryStore::fireEquipmentChangedEvent(const Ptr& actor)
// if player, update inventory window
/*
if (actor == MWMechanics::getPlayer())
if (mActor == MWMechanics::getPlayer())
{
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
}

View file

@ -67,9 +67,9 @@ namespace MWWorld
TSlots mSlots;
void autoEquipWeapon(const MWWorld::Ptr& actor, TSlots& slots_);
void autoEquipArmor(const MWWorld::Ptr& actor, TSlots& slots_);
void autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_);
void autoEquipWeapon(TSlots& slots_);
void autoEquipArmor(TSlots& slots_);
void autoEquipShield(TSlots& slots_);
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
ContainerStoreIterator mSelectedEnchantItem;
@ -78,7 +78,7 @@ namespace MWWorld
void initSlots(TSlots& slots_);
void fireEquipmentChangedEvent(const Ptr& actor);
void fireEquipmentChangedEvent();
void storeEquipmentState(
const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const override;
@ -94,10 +94,18 @@ namespace MWWorld
InventoryStore& operator=(const InventoryStore& store);
std::unique_ptr<ContainerStore> clone() override { return std::make_unique<InventoryStore>(*this); }
const MWWorld::Ptr& getActor() const { return mActor; }
void setActor(const MWWorld::Ptr& actor) { mActor = actor; }
ContainerStoreIterator add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true,
bool resolve = true) override;
std::unique_ptr<ContainerStore> clone() override
{
auto res = std::make_unique<InventoryStore>(*this);
res->clearRefNums();
return res;
}
ContainerStoreIterator add(
const Ptr& itemPtr, int count, 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).
///
@ -109,7 +117,7 @@ namespace MWWorld
/// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to
/// the newly inserted item.
void equip(int slot, const ContainerStoreIterator& iterator, const Ptr& actor);
void equip(int slot, const ContainerStoreIterator& iterator);
///< \warning \a iterator can not be an end()-iterator, use unequip function instead
bool isEquipped(const MWWorld::ConstPtr& item);
@ -126,30 +134,29 @@ namespace MWWorld
ContainerStoreIterator getSlot(int slot);
ConstContainerStoreIterator getSlot(int slot) const;
ContainerStoreIterator getPreferredShield(const MWWorld::Ptr& actor);
ContainerStoreIterator getPreferredShield();
void unequipAll(const MWWorld::Ptr& actor);
void unequipAll();
///< Unequip all currently equipped items.
void autoEquip(const MWWorld::Ptr& actor);
void autoEquip();
///< Auto equip items according to stats and item value.
bool stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const override;
///< @return true if the two specified objects can stack with each other
using ContainerStore::remove;
int remove(
const Ptr& item, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true) override;
int remove(const Ptr& item, int count, bool equipReplacement = 0, bool resolve = true) override;
///< Remove \a count item(s) designated by \a item from this inventory.
///
/// @return the number of items actually removed
ContainerStoreIterator unequipSlot(int slot, const Ptr& actor, bool applyUpdates = true);
ContainerStoreIterator unequipSlot(int slot, bool applyUpdates = true);
///< Unequip \a slot.
///
/// @return an iterator to the item that was previously in the slot
ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor);
ContainerStoreIterator unequipItem(const Ptr& item);
///< Unequip an item identified by its Ptr. An exception is thrown
/// if the item is not currently equipped.
///
@ -157,7 +164,7 @@ namespace MWWorld
/// (it can be re-stacked so its count may be different than when it
/// was equipped).
ContainerStoreIterator unequipItemQuantity(const Ptr& item, const Ptr& actor, int count);
ContainerStoreIterator unequipItemQuantity(const Ptr& item, int count);
///< Unequip a specific quantity of an item identified by its Ptr.
/// An exception is thrown if the item is not currently equipped,
/// if count <= 0, or if count > the item stack size.
@ -166,7 +173,7 @@ namespace MWWorld
/// in the slot (they can be re-stacked so its count may be different
/// than the requested count).
void setInvListener(InventoryStoreListener* listener, const Ptr& actor);
void setInvListener(InventoryStoreListener* listener);
///< Set a listener for various events, see \a InventoryStoreListener
InventoryStoreListener* getInvListener() const;

View file

@ -788,8 +788,6 @@ namespace MWWorld
void World::enable(const Ptr& reference)
{
MWBase::Environment::get().getWorldModel()->registerPtr(reference);
if (!reference.isInCell())
return;
@ -840,7 +838,6 @@ namespace MWWorld
if (reference == getPlayerPtr())
throw std::runtime_error("can not disable player object");
MWBase::Environment::get().getWorldModel()->deregisterPtr(reference);
reference.getRefData().disable();
if (reference.getCellRef().getRefNum().hasContentFile())
@ -2426,7 +2423,7 @@ namespace MWWorld
mRendering->renderPlayer(player);
MWRender::NpcAnimation* anim = static_cast<MWRender::NpcAnimation*>(mRendering->getAnimation(player));
player.getClass().getInventoryStore(player).setInvListener(anim, player);
player.getClass().getInventoryStore(player).setInvListener(anim);
player.getClass().getInventoryStore(player).setContListener(anim);
scaleObject(player, player.getCellRef().getScale(), true); // apply race height

View file

@ -80,7 +80,6 @@
-- @usage
-- # DataFiles/l10n/MyMod/en.yaml
-- good_morning: 'Good morning.'
--
-- you_have_arrows: |-
-- {count, plural,
-- one {You have one arrow.}
@ -107,11 +106,12 @@
-- Any object that exists in the game world and has a specific location.
-- Player, actors, items, and statics are game objects.
-- @type GameObject
-- @field #boolean enabled Whether the object is enabled or disabled. Global scripts can set the value. Items in containers or inventories can't be disabled.
-- @field openmw.util#Vector3 position Object position.
-- @field openmw.util#Vector3 rotation Object rotation (ZXY order).
-- @field #Cell cell The cell where the object currently is. During loading a game and for objects in an inventory or a container `cell` is nil.
-- @field #table type Type of the object (one of the tables from the package @{openmw.types#types}).
-- @field #number count Count (makes sense if stored in a container).
-- @field #number count Count (>1 means a stack of objects).
-- @field #string recordId Returns record ID of the object in lowercase.
---
@ -163,13 +163,39 @@
---
-- Moves object to given cell and position.
-- Can be called only from a global script.
-- The effect is not immediate: the position will be updated only in the next
-- frame. Can be called only from a global script.
-- frame. Can be called only from a global script. Enables object if it was disabled.
-- Can be used to move objects from an inventory or a container to the world.
-- @function [parent=#GameObject] teleport
-- @param self
-- @param #string cellName Name of the cell to teleport into. For exteriors can be empty.
-- @param openmw.util#Vector3 position New position
-- @param openmw.util#Vector3 rotation New rotation. Optional argument. If missed, then the current rotation is used.
-- @param openmw.util#Vector3 rotation New rotation. Optional argument. If missing, then the current rotation is used.
---
-- Moves object into a container or an inventory. Enables if was disabled.
-- Can be called only from a global script.
-- @function [parent=#GameObject] moveInto
-- @param self
-- @param #Inventory dest
-- @usage item:moveInto(types.Actor.inventory(actor))
---
-- Removes an object or reduces a stack of objects.
-- Can be called only from a global script.
-- @function [parent=#GameObject] remove
-- @param self
-- @param #number count (optional) the number of items to remove (if not specified then the whole stack)
---
-- Splits a stack of items. Original stack is reduced by `count`. Returns a new stack with `count` items.
-- Can be called only from a global script.
-- @function [parent=#GameObject] split
-- @param self
-- @param #number count The number of items to return.
-- @usage -- take 50 coins from `money` and put to the container `cont`
-- money:split(50):moveInto(types.Container.content(cont))
---
@ -246,6 +272,22 @@
-- local all = playerInventory:getAll()
-- local weapons = playerInventory:getAll(types.Weapon)
---
-- Get first item with given recordId from the inventory. Returns nil if not found.
-- @function [parent=#Inventory] find
-- @param self
-- @param #string recordId
-- @return #GameObject
-- @usage inventory:find('gold_001')
---
-- Get all items with given recordId from the inventory.
-- @function [parent=#Inventory] findAll
-- @param self
-- @param #string recordId
-- @return #ObjectList
-- @usage for _, item in ipairs(inventory:findAll('common_shirt_01')) do ... end
return nil

View file

@ -59,5 +59,19 @@
-- @function [parent=#world] isWorldPaused
-- @return #boolean
---
-- Create a new instance of the given record.
-- After creation the object is in the disabled state. Use :teleport to place to the world or :moveInto to put it into a container or an inventory.
-- @function [parent=#world] createObject
-- @param #string recordId Record ID in lowercase
-- @param #number count (optional, 1 by default) The number of objects in stack
-- @return openmw.core#GameObject
-- @usage -- put 100 gold on the ground at the position of `actor`
-- money = world.createObject('gold_001', 100)
-- money:teleport(actor.cell.name, actor.position)
-- @usage -- put 50 gold into the actor's inventory
-- money = world.createObject('gold_001', 50)
-- money:moveInto(types.Actor.inventory(actor))
return nil