1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-29 02:15:33 +00:00
openmw-tes3mp/apps/openmw/mwworld/containerstore.cpp
Tom Mason f785659297 Implemented OnPCAdd special variable
Had to edit OpAddItem in miscextensions.cpp, as local variables were not being initialised
for items added through it.
Does not get reset on drop, as per original morrowind.
2013-02-02 17:36:12 +00:00

611 lines
22 KiB
C++

#include "containerstore.hpp"
#include <cassert>
#include <typeinfo>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <components/esm/loadcont.hpp>
#include <components/compiler/locals.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "manualref.hpp"
#include "refdata.hpp"
#include "class.hpp"
#include "localscripts.hpp"
#include "player.hpp"
namespace
{
template<typename T>
float getTotalWeight (const MWWorld::CellRefList<T>& cellRefList)
{
float sum = 0;
for (typename MWWorld::CellRefList<T>::List::const_iterator iter (
cellRefList.mList.begin());
iter!=cellRefList.mList.end();
++iter)
{
if (iter->mData.getCount()>0)
sum += iter->mData.getCount()*iter->mBase->mData.mWeight;
}
return sum;
}
bool compare_string_ci(std::string str1, std::string str2)
{
Misc::StringUtils::toLower(str1);
return str1 == str2;
}
}
MWWorld::ContainerStore::ContainerStore() : mStateId (0), mCachedWeight (0), mWeightUpToDate (false) {}
MWWorld::ContainerStore::~ContainerStore() {}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask)
{
return ContainerStoreIterator (mask, this);
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()
{
return ContainerStoreIterator (this);
}
bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2)
{
/// \todo add current weapon/armor health, remaining lockpick/repair uses, current enchantment charge here as soon as they are implemented
if ( ptr1.mCellRef->mRefID == ptr2.mCellRef->mRefID
&& MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks
&& MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier)
&& ptr1.mCellRef->mOwner == ptr2.mCellRef->mOwner
&& ptr1.mCellRef->mSoul == ptr2.mCellRef->mSoul
&& ptr1.mCellRef->mCharge == ptr2.mCellRef->mCharge)
return true;
return false;
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr)
{
MWWorld::ContainerStoreIterator it = addImp(ptr);
MWWorld::Ptr item = *it;
std::string script = MWWorld::Class::get(item).getScript(item);
if(script != "")
{
CellStore *cell;
Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer();
if(&(MWWorld::Class::get (player).getContainerStore (player)) == this)
{
cell = 0; // Items in players inventory have cell set to 0, so their scripts will never be removed
// Set OnPCAdd special variable, if it is declared
Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script);
int index = locals.getIndex("onpcadd");
char type = locals.getType("onpcadd");
if(index != -1)
{
switch(type)
{
case 's':
item.mRefData->getLocals().mShorts.at (index) = 1; break;
case 'l':
item.mRefData->getLocals().mLongs.at (index) = 1; break;
case 'f':
item.mRefData->getLocals().mFloats.at (index) = 1.0; break;
}
}
}
else
cell = player.getCell();
item.mCell = cell;
item.mContainerStore = 0;
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item);
}
return it;
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr)
{
int type = getType(ptr);
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
// gold needs special handling: when it is inserted into a container, the base object automatically becomes Gold_001
// this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold)
if (MWWorld::Class::get(ptr).getName(ptr) == esmStore.get<ESM::GameSetting>().find("sGold")->getString())
{
MWWorld::LiveCellRef<ESM::Miscellaneous> *gold =
ptr.get<ESM::Miscellaneous>();
if (compare_string_ci(gold->mRef.mRefID, "gold_001")
|| compare_string_ci(gold->mRef.mRefID, "gold_005")
|| compare_string_ci(gold->mRef.mRefID, "gold_010")
|| compare_string_ci(gold->mRef.mRefID, "gold_025")
|| compare_string_ci(gold->mRef.mRefID, "gold_100"))
{
MWWorld::ManualRef ref(esmStore, "Gold_001");
int count = (ptr.getRefData().getCount() == 1) ? gold->mBase->mData.mValue : ptr.getRefData().getCount();
ref.getPtr().getRefData().setCount(count);
for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter)
{
if (compare_string_ci((*iter).get<ESM::Miscellaneous>()->mRef.mRefID, "gold_001"))
{
(*iter).getRefData().setCount( (*iter).getRefData().getCount() + count);
flagAsModified();
return iter;
}
}
return addImpl(ref.getPtr());
}
}
// determine whether to stack or not
for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter)
{
if (stacks(*iter, ptr))
{
// stack
iter->getRefData().setCount( iter->getRefData().getCount() + ptr.getRefData().getCount() );
flagAsModified();
return iter;
}
}
// if we got here, this means no stacking
return addImpl(ptr);
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr)
{
ContainerStoreIterator it = begin();
switch (getType(ptr))
{
case Type_Potion: potions.mList.push_back (*ptr.get<ESM::Potion>()); it = ContainerStoreIterator(this, --potions.mList.end()); break;
case Type_Apparatus: appas.mList.push_back (*ptr.get<ESM::Apparatus>()); it = ContainerStoreIterator(this, --appas.mList.end()); break;
case Type_Armor: armors.mList.push_back (*ptr.get<ESM::Armor>()); it = ContainerStoreIterator(this, --armors.mList.end()); break;
case Type_Book: books.mList.push_back (*ptr.get<ESM::Book>()); it = ContainerStoreIterator(this, --books.mList.end()); break;
case Type_Clothing: clothes.mList.push_back (*ptr.get<ESM::Clothing>()); it = ContainerStoreIterator(this, --clothes.mList.end()); break;
case Type_Ingredient: ingreds.mList.push_back (*ptr.get<ESM::Ingredient>()); it = ContainerStoreIterator(this, --ingreds.mList.end()); break;
case Type_Light: lights.mList.push_back (*ptr.get<ESM::Light>()); it = ContainerStoreIterator(this, --lights.mList.end()); break;
case Type_Lockpick: lockpicks.mList.push_back (*ptr.get<ESM::Tool>()); it = ContainerStoreIterator(this, --lockpicks.mList.end()); break;
case Type_Miscellaneous: miscItems.mList.push_back (*ptr.get<ESM::Miscellaneous>()); it = ContainerStoreIterator(this, --miscItems.mList.end()); break;
case Type_Probe: probes.mList.push_back (*ptr.get<ESM::Probe>()); it = ContainerStoreIterator(this, --probes.mList.end()); break;
case Type_Repair: repairs.mList.push_back (*ptr.get<ESM::Repair>()); it = ContainerStoreIterator(this, --repairs.mList.end()); break;
case Type_Weapon: weapons.mList.push_back (*ptr.get<ESM::Weapon>()); it = ContainerStoreIterator(this, --weapons.mList.end()); break;
}
flagAsModified();
return it;
}
void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const MWWorld::ESMStore& store)
{
for (std::vector<ESM::ContItem>::const_iterator iter (items.mList.begin()); iter!=items.mList.end();
++iter)
{
ManualRef ref (store, iter->mItem.toString());
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
{
/// \todo implement leveled item lists
continue;
}
ref.getPtr().getRefData().setCount (std::abs(iter->mCount)); /// \todo implement item restocking (indicated by negative count)
addImp (ref.getPtr());
}
flagAsModified();
}
void MWWorld::ContainerStore::clear()
{
for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
iter->getRefData().setCount (0);
flagAsModified();
}
void MWWorld::ContainerStore::flagAsModified()
{
++mStateId;
mWeightUpToDate = false;
}
int MWWorld::ContainerStore::getStateId() const
{
return mStateId;
}
float MWWorld::ContainerStore::getWeight() const
{
if (!mWeightUpToDate)
{
mCachedWeight = 0;
mCachedWeight += getTotalWeight (potions);
mCachedWeight += getTotalWeight (appas);
mCachedWeight += getTotalWeight (armors);
mCachedWeight += getTotalWeight (books);
mCachedWeight += getTotalWeight (clothes);
mCachedWeight += getTotalWeight (ingreds);
mCachedWeight += getTotalWeight (lights);
mCachedWeight += getTotalWeight (lockpicks);
mCachedWeight += getTotalWeight (miscItems);
mCachedWeight += getTotalWeight (probes);
mCachedWeight += getTotalWeight (repairs);
mCachedWeight += getTotalWeight (weapons);
mWeightUpToDate = true;
}
return mCachedWeight;
}
int MWWorld::ContainerStore::getType (const Ptr& ptr)
{
if (ptr.isEmpty())
throw std::runtime_error ("can't put a non-existent object into a container");
if (ptr.getTypeName()==typeid (ESM::Potion).name())
return Type_Potion;
if (ptr.getTypeName()==typeid (ESM::Apparatus).name())
return Type_Apparatus;
if (ptr.getTypeName()==typeid (ESM::Armor).name())
return Type_Armor;
if (ptr.getTypeName()==typeid (ESM::Book).name())
return Type_Book;
if (ptr.getTypeName()==typeid (ESM::Clothing).name())
return Type_Clothing;
if (ptr.getTypeName()==typeid (ESM::Ingredient).name())
return Type_Ingredient;
if (ptr.getTypeName()==typeid (ESM::Light).name())
return Type_Light;
if (ptr.getTypeName()==typeid (ESM::Tool).name())
return Type_Lockpick;
if (ptr.getTypeName()==typeid (ESM::Miscellaneous).name())
return Type_Miscellaneous;
if (ptr.getTypeName()==typeid (ESM::Probe).name())
return Type_Probe;
if (ptr.getTypeName()==typeid (ESM::Repair).name())
return Type_Repair;
if (ptr.getTypeName()==typeid (ESM::Weapon).name())
return Type_Weapon;
throw std::runtime_error (
"Object of type " + ptr.getTypeName() + " can not be placed into a container");
}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container)
: mType (-1), mMask (0), mContainer (container)
{}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (int mask, ContainerStore *container)
: mType (0), mMask (mask), mContainer (container)
{
nextType();
if (mType==-1 || (**this).getRefData().getCount())
return;
++*this;
}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Potion>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Potion), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mPotion(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Apparatus>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Apparatus), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mApparatus(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Armor>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Armor), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mArmor(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Book>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Book), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mBook(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Clothing>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Clothing), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mClothing(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Ingredient>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Ingredient), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mIngredient(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Light>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Light), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLight(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Tool>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Lockpick), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLockpick(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Miscellaneous>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Miscellaneous), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mMiscellaneous(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Probe>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Probe), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mProbe(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Repair>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Repair), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mRepair(iterator){}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Weapon>::List::iterator iterator)
: mType(MWWorld::ContainerStore::Type_Weapon), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mWeapon(iterator){}
void MWWorld::ContainerStoreIterator::incType()
{
if (mType==0)
mType = 1;
else if (mType!=-1)
{
mType <<= 1;
if (mType>ContainerStore::Type_Last)
mType = -1;
}
}
void MWWorld::ContainerStoreIterator::nextType()
{
while (mType!=-1)
{
incType();
if ((mType & mMask) && mType>0)
if (resetIterator())
break;
}
}
bool MWWorld::ContainerStoreIterator::resetIterator()
{
switch (mType)
{
case ContainerStore::Type_Potion:
mPotion = mContainer->potions.mList.begin();
return mPotion!=mContainer->potions.mList.end();
case ContainerStore::Type_Apparatus:
mApparatus = mContainer->appas.mList.begin();
return mApparatus!=mContainer->appas.mList.end();
case ContainerStore::Type_Armor:
mArmor = mContainer->armors.mList.begin();
return mArmor!=mContainer->armors.mList.end();
case ContainerStore::Type_Book:
mBook = mContainer->books.mList.begin();
return mBook!=mContainer->books.mList.end();
case ContainerStore::Type_Clothing:
mClothing = mContainer->clothes.mList.begin();
return mClothing!=mContainer->clothes.mList.end();
case ContainerStore::Type_Ingredient:
mIngredient = mContainer->ingreds.mList.begin();
return mIngredient!=mContainer->ingreds.mList.end();
case ContainerStore::Type_Light:
mLight = mContainer->lights.mList.begin();
return mLight!=mContainer->lights.mList.end();
case ContainerStore::Type_Lockpick:
mLockpick = mContainer->lockpicks.mList.begin();
return mLockpick!=mContainer->lockpicks.mList.end();
case ContainerStore::Type_Miscellaneous:
mMiscellaneous = mContainer->miscItems.mList.begin();
return mMiscellaneous!=mContainer->miscItems.mList.end();
case ContainerStore::Type_Probe:
mProbe = mContainer->probes.mList.begin();
return mProbe!=mContainer->probes.mList.end();
case ContainerStore::Type_Repair:
mRepair = mContainer->repairs.mList.begin();
return mRepair!=mContainer->repairs.mList.end();
case ContainerStore::Type_Weapon:
mWeapon = mContainer->weapons.mList.begin();
return mWeapon!=mContainer->weapons.mList.end();
}
return false;
}
bool MWWorld::ContainerStoreIterator::incIterator()
{
switch (mType)
{
case ContainerStore::Type_Potion:
++mPotion;
return mPotion==mContainer->potions.mList.end();
case ContainerStore::Type_Apparatus:
++mApparatus;
return mApparatus==mContainer->appas.mList.end();
case ContainerStore::Type_Armor:
++mArmor;
return mArmor==mContainer->armors.mList.end();
case ContainerStore::Type_Book:
++mBook;
return mBook==mContainer->books.mList.end();
case ContainerStore::Type_Clothing:
++mClothing;
return mClothing==mContainer->clothes.mList.end();
case ContainerStore::Type_Ingredient:
++mIngredient;
return mIngredient==mContainer->ingreds.mList.end();
case ContainerStore::Type_Light:
++mLight;
return mLight==mContainer->lights.mList.end();
case ContainerStore::Type_Lockpick:
++mLockpick;
return mLockpick==mContainer->lockpicks.mList.end();
case ContainerStore::Type_Miscellaneous:
++mMiscellaneous;
return mMiscellaneous==mContainer->miscItems.mList.end();
case ContainerStore::Type_Probe:
++mProbe;
return mProbe==mContainer->probes.mList.end();
case ContainerStore::Type_Repair:
++mRepair;
return mRepair==mContainer->repairs.mList.end();
case ContainerStore::Type_Weapon:
++mWeapon;
return mWeapon==mContainer->weapons.mList.end();
}
return true;
}
MWWorld::Ptr *MWWorld::ContainerStoreIterator::operator->() const
{
mPtr = **this;
return &mPtr;
}
MWWorld::Ptr MWWorld::ContainerStoreIterator::operator*() const
{
Ptr ptr;
switch (mType)
{
case ContainerStore::Type_Potion: ptr = MWWorld::Ptr (&*mPotion, 0); break;
case ContainerStore::Type_Apparatus: ptr = MWWorld::Ptr (&*mApparatus, 0); break;
case ContainerStore::Type_Armor: ptr = MWWorld::Ptr (&*mArmor, 0); break;
case ContainerStore::Type_Book: ptr = MWWorld::Ptr (&*mBook, 0); break;
case ContainerStore::Type_Clothing: ptr = MWWorld::Ptr (&*mClothing, 0); break;
case ContainerStore::Type_Ingredient: ptr = MWWorld::Ptr (&*mIngredient, 0); break;
case ContainerStore::Type_Light: ptr = MWWorld::Ptr (&*mLight, 0); break;
case ContainerStore::Type_Lockpick: ptr = MWWorld::Ptr (&*mLockpick, 0); break;
case ContainerStore::Type_Miscellaneous: ptr = MWWorld::Ptr (&*mMiscellaneous, 0); break;
case ContainerStore::Type_Probe: ptr = MWWorld::Ptr (&*mProbe, 0); break;
case ContainerStore::Type_Repair: ptr = MWWorld::Ptr (&*mRepair, 0); break;
case ContainerStore::Type_Weapon: ptr = MWWorld::Ptr (&*mWeapon, 0); break;
}
if (ptr.isEmpty())
throw std::runtime_error ("invalid iterator");
ptr.setContainerStore (mContainer);
return ptr;
}
MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator++()
{
do
{
if (incIterator())
nextType();
}
while (mType!=-1 && !(**this).getRefData().getCount());
return *this;
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStoreIterator::operator++ (int)
{
ContainerStoreIterator iter (*this);
++*this;
return iter;
}
bool MWWorld::ContainerStoreIterator::isEqual (const ContainerStoreIterator& iter) const
{
assert (mContainer==iter.mContainer);
if (mType!=iter.mType)
return false;
switch (mType)
{
case ContainerStore::Type_Potion: return mPotion==iter.mPotion;
case ContainerStore::Type_Apparatus: return mApparatus==iter.mApparatus;
case ContainerStore::Type_Armor: return mArmor==iter.mArmor;
case ContainerStore::Type_Book: return mBook==iter.mBook;
case ContainerStore::Type_Clothing: return mClothing==iter.mClothing;
case ContainerStore::Type_Ingredient: return mIngredient==iter.mIngredient;
case ContainerStore::Type_Light: return mLight==iter.mLight;
case ContainerStore::Type_Lockpick: return mLockpick==iter.mLockpick;
case ContainerStore::Type_Miscellaneous: return mMiscellaneous==iter.mMiscellaneous;
case ContainerStore::Type_Probe: return mProbe==iter.mProbe;
case ContainerStore::Type_Repair: return mRepair==iter.mRepair;
case ContainerStore::Type_Weapon: return mWeapon==iter.mWeapon;
case -1: return true;
}
return false;
}
int MWWorld::ContainerStoreIterator::getType() const
{
return mType;
}
const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStore() const
{
return mContainer;
}
bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right)
{
return left.isEqual (right);
}
bool MWWorld::operator!= (const ContainerStoreIterator& left, const ContainerStoreIterator& right)
{
return !(left==right);
}