Add `obj.parentContainer` in Lua. Refactor ContainerStore::mPtr, ContainerStore::mActor.

macos_ci_fix
Petr Mikheev 1 year ago
parent 518aa5c6ae
commit 55a9ab4f52

@ -21,6 +21,7 @@
#include "../mwworld/failedaction.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwworld/worldmodel.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwgui/ustring.hpp"
@ -68,10 +69,12 @@ namespace MWClass
{
if (!ptr.getRefData().getCustomData())
{
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
MWWorld::LiveCellRef<ESM::Container>* ref = ptr.get<ESM::Container>();
// store
ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()));
getContainerStore(ptr).setPtr(ptr);
MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());
}
@ -223,9 +226,7 @@ namespace MWClass
MWWorld::ContainerStore& Container::getContainerStore(const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
auto& data = ptr.getRefData().getCustomData()->asContainerCustomData();
data.mStore.mPtr = ptr;
return data.mStore;
return ptr.getRefData().getCustomData()->asContainerCustomData().mStore;
}
ESM::RefId Container::getScript(const MWWorld::ConstPtr& ptr) const
@ -312,6 +313,9 @@ namespace MWClass
const ESM::ContainerState& containerState = state.asContainerState();
ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory));
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
getContainerStore(ptr).setPtr(ptr);
}
void Container::writeAdditionalState(const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const

@ -39,6 +39,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/localscripts.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/worldmodel.hpp"
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
@ -117,6 +118,7 @@ namespace MWClass
{
if (!ptr.getRefData().getCustomData())
{
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
auto tempData = std::make_unique<CreatureCustomData>();
CreatureCustomData* data = tempData.get();
MWMechanics::CreatureCustomDataResetter resetter{ ptr };
@ -161,6 +163,7 @@ namespace MWClass
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
else
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
data->mContainerStore->setPtr(ptr);
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
@ -505,10 +508,7 @@ namespace MWClass
MWWorld::ContainerStore& Creature::getContainerStore(const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
auto& store = *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore;
if (hasInventoryStore(ptr))
static_cast<MWWorld::InventoryStore&>(store).setActor(ptr);
return store;
return *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore;
}
MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr& ptr) const
@ -807,6 +807,9 @@ namespace MWClass
else
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
data->mContainerStore->setPtr(ptr);
ptr.getRefData().setCustomData(std::move(data));
}
}

@ -297,6 +297,7 @@ namespace MWClass
{
if (!ptr.getRefData().getCustomData())
{
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
bool recalculate = false;
auto tempData = std::make_unique<NpcCustomData>();
NpcCustomData* data = tempData.get();
@ -397,9 +398,10 @@ namespace MWClass
// inventory
// setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
getInventoryStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId(), prng);
getInventoryStore(ptr).autoEquip();
MWWorld::InventoryStore& inventory = getInventoryStore(ptr);
inventory.setPtr(ptr);
inventory.fill(ref->mBase->mInventory, ptr.getCellRef().getRefId(), prng);
inventory.autoEquip();
}
}
@ -956,18 +958,13 @@ namespace MWClass
MWWorld::ContainerStore& Npc::getContainerStore(const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
auto& store = ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;
store.setActor(ptr);
return store;
return getInventoryStore(ptr);
}
MWWorld::InventoryStore& Npc::getInventoryStore(const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
auto& store = ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;
store.setActor(ptr);
return store;
return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;
}
ESM::RefId Npc::getScript(const MWWorld::ConstPtr& ptr) const
@ -1362,8 +1359,13 @@ namespace MWClass
if (npcState.mCreatureStats.mMissingACDT)
ensureCustomData(ptr);
else
{
// Create a CustomData, but don't fill it from ESM records (not needed)
ptr.getRefData().setCustomData(std::make_unique<NpcCustomData>());
auto data = std::make_unique<NpcCustomData>();
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
data->mInventoryStore.setPtr(ptr);
ptr.getRefData().setCustomData(std::move(data));
}
}
}
else

@ -190,6 +190,13 @@ namespace MWLua
else
return sol::nullopt;
});
objectT["parentContainer"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<ObjectT> {
const MWWorld::Ptr& ptr = o.ptr();
if (ptr.getContainerStore())
return ObjectT(ptr.getContainerStore()->getPtr());
else
return sol::nullopt;
});
objectT["position"] = sol::readonly_property(
[](const ObjectT& o) -> osg::Vec3f { return o.ptr().getRefData().getPosition().asVec3(); });
objectT["scale"]

@ -495,11 +495,7 @@ namespace MWWorld
mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo));
requestMergedRefsUpdate();
MWWorld::Ptr ptr(object.getBase(), cellToMoveTo);
const Class& cls = ptr.getClass();
if (cls.hasInventoryStore(ptr))
cls.getInventoryStore(ptr).setActor(ptr);
return ptr;
return MWWorld::Ptr(object.getBase(), cellToMoveTo);
}
struct MergeVisitor

@ -376,8 +376,6 @@ namespace MWWorld
newPtr.getRefData().setCount(count);
newPtr.getRefData().setLuaScripts(nullptr);
MWBase::Environment::get().getWorldModel()->registerPtr(newPtr);
if (hasInventoryStore(newPtr))
getInventoryStore(newPtr).setActor(newPtr);
return newPtr;
}
@ -386,8 +384,6 @@ namespace MWWorld
Ptr newPtr = copyToCellImpl(ptr, cell);
ptr.getRefData().setLuaScripts(nullptr);
MWBase::Environment::get().getWorldModel()->registerPtr(newPtr);
if (hasInventoryStore(newPtr))
getInventoryStore(newPtr).setActor(newPtr);
return newPtr;
}

@ -350,7 +350,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(
const ESM::RefId& script = item.getClass().getScript(item);
if (!script.empty())
{
if (mActor == player)
const Ptr& contPtr = getPtr();
if (contPtr == player)
{
// Items in player's inventory have cell set to 0, so their scripts will never be removed
item.mCell = nullptr;
@ -359,10 +360,8 @@ 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
if (!mPtr.isEmpty())
item.mCell = mPtr.getCell();
else if (!mActor.isEmpty())
item.mCell = mActor.getCell();
if (!contPtr.isEmpty())
item.mCell = contPtr.getCell();
}
item.mContainerStore = this;
@ -371,12 +370,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 (mActor == player)
if (contPtr == player)
item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1);
}
// we should not fire event for InventoryStore yet - it has some custom logic
if (mListener && !(!mActor.isEmpty() && mActor.getClass().hasInventoryStore(mActor)))
if (mListener && typeid(*this) == typeid(ContainerStore))
mListener->itemAdded(item, count);
return it;
@ -415,8 +414,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp(const Ptr& ptr,
for (MWWorld::ContainerStoreIterator iter(begin(type)); iter != end(); ++iter)
{
// Don't stack with equipped items
if (!mActor.isEmpty() && mActor.getClass().hasInventoryStore(mActor))
if (mActor.getClass().getInventoryStore(mActor).isEquipped(*iter))
if (auto* inventoryStore = dynamic_cast<InventoryStore*>(this))
if (inventoryStore->isEquipped(*iter))
continue;
if (stacks(*iter, ptr))
@ -586,7 +585,7 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, bool equipReplac
flagAsModified();
// we should not fire event for InventoryStore yet - it has some custom logic
if (mListener && !(!mActor.isEmpty() && mActor.getClass().hasInventoryStore(mActor)))
if (mListener && typeid(*this) == typeid(ContainerStore))
mListener->itemRemoved(item, count - toRemove);
// number of removed items
@ -694,13 +693,14 @@ bool MWWorld::ContainerStore::isResolved() const
void MWWorld::ContainerStore::resolve()
{
if (!mResolved && !mPtr.isEmpty())
const Ptr& container = getPtr();
if (!mResolved && !container.isEmpty() && container.getType() == ESM::REC_CONT)
{
for (const auto&& ptr : *this)
ptr.getRefData().setCount(0);
Misc::Rng::Generator prng{ mSeed };
fill(mPtr.get<ESM::Container>()->mBase->mInventory, ESM::RefId(), prng);
addScripts(*this, mPtr.mCell);
fill(container.get<ESM::Container>()->mBase->mInventory, ESM::RefId(), prng);
addScripts(*this, container.mCell);
}
mModified = true;
}
@ -715,13 +715,14 @@ MWWorld::ResolutionHandle MWWorld::ContainerStore::resolveTemporarily()
listener = std::make_shared<ResolutionListener>(*this);
mResolutionListener = listener;
}
if (!mResolved && !mPtr.isEmpty())
const Ptr& container = getPtr();
if (!mResolved && !container.isEmpty() && container.getType() == ESM::REC_CONT)
{
for (const auto&& ptr : *this)
ptr.getRefData().setCount(0);
Misc::Rng::Generator prng{ mSeed };
fill(mPtr.get<ESM::Container>()->mBase->mInventory, ESM::RefId(), prng);
addScripts(*this, mPtr.mCell);
fill(container.get<ESM::Container>()->mBase->mInventory, ESM::RefId(), prng);
addScripts(*this, container.mCell);
}
return { listener };
}
@ -731,12 +732,13 @@ void MWWorld::ContainerStore::unresolve()
if (mModified)
return;
if (mResolved && !mPtr.isEmpty())
const Ptr& container = getPtr();
if (mResolved && !container.isEmpty() && container.getType() == ESM::REC_CONT)
{
for (const auto&& ptr : *this)
ptr.getRefData().setCount(0);
fillNonRandom(mPtr.get<ESM::Container>()->mBase->mInventory, ESM::RefId(), mSeed);
addScripts(*this, mPtr.mCell);
fillNonRandom(container.get<ESM::Container>()->mBase->mInventory, ESM::RefId(), mSeed);
addScripts(*this, container.mCell);
mResolved = false;
}
}

@ -125,11 +125,6 @@ namespace MWWorld
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;
@ -150,7 +145,7 @@ namespace MWWorld
bool mModified;
bool mResolved;
unsigned int mSeed;
MWWorld::Ptr mPtr; // Container that contains this store. Set in MWClass::Container::getContainerStore
MWWorld::SafePtr mPtr; // Container or actor that holds this store.
std::weak_ptr<ResolutionListener> mResolutionListener;
ContainerStoreIterator addImp(const Ptr& ptr, int count, bool markModified = true);
@ -189,6 +184,10 @@ namespace MWWorld
return res;
}
// Container or actor that holds this store.
const Ptr& getPtr() const { return mPtr.ptrOrEmpty(); }
void setPtr(const Ptr& ptr) { mPtr = SafePtr(ptr); }
ConstContainerStoreIterator cbegin(int mask = Type_All) const;
ConstContainerStoreIterator cend() const;
ConstContainerStoreIterator begin(int mask = Type_All) const;

@ -133,8 +133,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(
= 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 && mActor != MWMechanics::getPlayer() && mActor.getClass().isNpc()
&& !mActor.getClass().getNpcStats(mActor).isWerewolf())
const Ptr& actor = getPtr();
if (allowAutoEquip && actor != MWMechanics::getPlayer() && actor.getClass().isNpc()
&& !actor.getClass().getNpcStats(actor).isWerewolf())
{
auto type = itemPtr.getType();
if (type == ESM::Armor::sRecordId || type == ESM::Clothing::sRecordId)
@ -220,12 +221,13 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot(int slot) cons
void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
{
if (!mActor.getClass().isNpc())
const Ptr& actor = getPtr();
if (!actor.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 = mActor.getClass().getServices(mActor);
int services = actor.getClass().getServices(actor);
bool sellsWeapon = services & (ESM::NPC::Weapon | ESM::NPC::MagicItems);
if (sellsWeapon)
return;
@ -280,7 +282,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
{
float skillValue = mActor.getClass().getSkill(mActor, weaponSkills[j]);
float skillValue = actor.getClass().getSkill(actor, weaponSkills[j]);
if (skillValue > max && !weaponSkillVisited[j])
{
max = skillValue;
@ -323,7 +325,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
}
}
if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, mActor).first)
if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first)
{
// Do not equip ranged weapons, if there is no suitable ammo
bool hasAmmo = true;
@ -378,7 +380,8 @@ void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
{
// Only NPCs can wear armor for now.
// For creatures we equip only shields.
if (!mActor.getClass().isNpc())
const Ptr& actor = getPtr();
if (!actor.getClass().isNpc())
{
autoEquipShield(slots_);
return;
@ -389,7 +392,7 @@ void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
float unarmoredSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Unarmored);
float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored);
float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter != end();
@ -397,7 +400,7 @@ void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
{
Ptr test = *iter;
switch (test.getClass().canBeEquipped(test, mActor).first)
switch (test.getClass().canBeEquipped(test, actor).first)
{
case 0:
continue;
@ -406,7 +409,7 @@ void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
}
if (iter.getType() == ContainerStore::Type_Armor
&& test.getClass().getEffectiveArmorRating(test, mActor) <= std::max(unarmoredRating, 0.f))
&& test.getClass().getEffectiveArmorRating(test, actor) <= std::max(unarmoredRating, 0.f))
{
continue;
}
@ -431,8 +434,8 @@ void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
if (old.get<ESM::Armor>()->mBase->mData.mType == test.get<ESM::Armor>()->mBase->mData.mType)
{
if (old.getClass().getEffectiveArmorRating(old, mActor)
>= test.getClass().getEffectiveArmorRating(test, mActor))
if (old.getClass().getEffectiveArmorRating(old, actor)
>= test.getClass().getEffectiveArmorRating(test, actor))
// old armor had better armor rating
continue;
}
@ -494,7 +497,7 @@ void MWWorld::InventoryStore::autoEquipShield(TSlots& slots_)
{
if (iter->get<ESM::Armor>()->mBase->mData.mType != ESM::Armor::Shield)
continue;
if (iter->getClass().canBeEquipped(*iter, mActor).first != 1)
if (iter->getClass().canBeEquipped(*iter, getPtr()).first != 1)
continue;
std::pair<std::vector<int>, bool> shieldSlots = iter->getClass().getEquipmentSlots(*iter);
int slot = shieldSlots.first[0];
@ -606,8 +609,9 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, bool equipReplac
// 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 && (mActor != MWMechanics::getPlayer()) && mActor.getClass().isNpc()
&& !mActor.getClass().getNpcStats(mActor).isWerewolf())
const Ptr& actor = getPtr();
if (equipReplacement && wasEquipped && (actor != MWMechanics::getPlayer()) && actor.getClass().isNpc()
&& !actor.getClass().getNpcStats(actor).isWerewolf())
{
auto type = item.getType();
if (type == ESM::Armor::sRecordId || type == ESM::Clothing::sRecordId)
@ -643,7 +647,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, b
{
retval = restack(*it);
if (mActor == MWMechanics::getPlayer())
if (getPtr() == 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);

@ -94,9 +94,6 @@ namespace MWWorld
InventoryStore& operator=(const InventoryStore& store);
const MWWorld::Ptr& getActor() const { return mActor; }
void setActor(const MWWorld::Ptr& actor) { mActor = actor; }
std::unique_ptr<ContainerStore> clone() override
{
auto res = std::make_unique<InventoryStore>(*this);

@ -153,8 +153,9 @@ namespace MWWorld
class SafePtr
{
public:
using Id = ESM::RefNum;
using Id = ESM::FormId;
SafePtr() = default;
explicit SafePtr(Id id)
: mId(id)
{
@ -172,7 +173,7 @@ namespace MWWorld
}
private:
const Id mId;
Id mId;
mutable Ptr mPtr;
mutable size_t mLastUpdate = 0;

@ -159,6 +159,7 @@
-- @field #string ownerFactionId Faction who owns the object (nil if missing). Global and self scripts can set the value.
-- @field #number ownerFactionRank Rank required to be allowed to pick up the object. Global and self scripts can set the value.
-- @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 #GameObject parentContainer Container or actor that contains (or has in inventory) this object. It is nil if the object is in a cell.
-- @field #any type Type of the object (one of the tables from the package @{openmw.types#types}).
-- @field #number count Count (>1 means a stack of objects).
-- @field #string recordId Returns record ID of the object in lowercase.

Loading…
Cancel
Save