////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include "container.h"
#include "game.h"
#include "iomap.h"
#include "player.h"
extern Game g_game;
Container::Container(uint16_t _type) : Item(_type)
{
//std::cout << "Container constructor " << this << std::endl;
maxSize = items[this->getID()].maxItems;
totalWeight = 0.0;
}
Container::~Container()
{
//std::cout << "Container destructor " << this << std::endl;
for(ItemList::iterator cit = itemlist.begin(); cit != itemlist.end(); ++cit)
{
(*cit)->setParent(NULL);
(*cit)->releaseThing2();
}
itemlist.clear();
}
Item* Container::clone() const
{
Container* _item = static_cast(Item::clone());
for(ItemList::const_iterator it = itemlist.begin(); it != itemlist.end(); ++it)
_item->addItem((*it)->clone());
return _item;
}
Container* Container::getParentContainer()
{
if(Thing* thing = getParent())
{
if(Item* item = thing->getItem())
return item->getContainer();
}
return NULL;
}
void Container::addItem(Item* item)
{
itemlist.push_back(item);
item->setParent(this);
}
bool Container::unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream)
{
if(Item::unserializeItemNode(f, node, propStream))
{
uint32_t type;
NODE nodeItem = f.getChildNode(node, type);
while(nodeItem)
{
//load container items
if(type == OTBM_ITEM)
{
PropStream itemPropStream;
f.getProps(nodeItem, itemPropStream);
Item* item = Item::CreateItem(itemPropStream);
if(!item)
return false;
if(!item->unserializeItemNode(f, nodeItem, itemPropStream))
return false;
addItem(item);
totalWeight += item->getWeight();
if(Container* parent = getParentContainer())
parent->updateItemWeight(item->getWeight());
}
else/*unknown type*/
return false;
nodeItem = f.getNextNode(nodeItem, type);
}
return true;
}
return false;
}
void Container::updateItemWeight(double diff)
{
totalWeight += diff;
if(Container* parentContainer = getParentContainer())
parentContainer->updateItemWeight(diff);
}
double Container::getWeight() const
{
return Item::getWeight() + totalWeight;
}
std::string Container::getContentDescription() const
{
std::stringstream s;
return getContentDescription(s).str();
}
std::stringstream& Container::getContentDescription(std::stringstream& s) const
{
bool begin = true;
Container* evil = const_cast(this);
for(ContainerIterator it = evil->begin(); it != evil->end(); ++it)
{
if(!begin)
s << ", ";
else
begin = false;
s << (*it)->getNameDescription();
}
if(begin)
s << "nothing";
return s;
}
Item* Container::getItem(uint32_t index)
{
size_t n = 0;
for(ItemList::const_iterator cit = getItems(); cit != getEnd(); ++cit)
{
if(n == index)
return *cit;
else
++n;
}
return NULL;
}
uint32_t Container::getItemHoldingCount() const
{
uint32_t counter = 0;
for(ContainerIterator it = begin(); it != end(); ++it)
++counter;
return counter;
}
bool Container::isHoldingItem(const Item* item) const
{
for(ContainerIterator it = begin(); it != end(); ++it)
{
if((*it) == item)
return true;
}
return false;
}
void Container::onAddContainerItem(Item* item)
{
const Position& cylinderMapPos = getPosition();
SpectatorVec list;
SpectatorVec::iterator it;
g_game.getSpectators(list, cylinderMapPos, false, false, 2, 2, 2, 2);
//send to client
Player* player = NULL;
for(it = list.begin(); it != list.end(); ++it)
{
if((player = (*it)->getPlayer()))
player->sendAddContainerItem(this, item);
}
//event methods
for(it = list.begin(); it != list.end(); ++it)
{
if((player = (*it)->getPlayer()))
player->onAddContainerItem(this, item);
}
}
void Container::onUpdateContainerItem(uint32_t index, Item* oldItem, const ItemType& oldType,
Item* newItem, const ItemType& newType)
{
const Position& cylinderMapPos = getPosition();
SpectatorVec list;
SpectatorVec::iterator it;
g_game.getSpectators(list, cylinderMapPos, false, false, 2, 2, 2, 2);
//send to client
Player* player = NULL;
for(it = list.begin(); it != list.end(); ++it)
{
if((player = (*it)->getPlayer()))
player->sendUpdateContainerItem(this, index, oldItem, newItem);
}
//event methods
for(it = list.begin(); it != list.end(); ++it)
{
if((player = (*it)->getPlayer()))
player->onUpdateContainerItem(this, index, oldItem, oldType, newItem, newType);
}
}
void Container::onRemoveContainerItem(uint32_t index, Item* item)
{
const Position& cylinderMapPos = getPosition();
SpectatorVec list;
SpectatorVec::iterator it;
g_game.getSpectators(list, cylinderMapPos, false, false, 2, 2, 2, 2);
//send change to client
Player* player = NULL;
for(it = list.begin(); it != list.end(); ++it)
{
if((player = (*it)->getPlayer()))
player->sendRemoveContainerItem(this, index, item);
}
//event methods
for(it = list.begin(); it != list.end(); ++it)
{
if((player = (*it)->getPlayer()))
player->onRemoveContainerItem(this, index, item);
}
}
ReturnValue Container::__queryAdd(int32_t index, const Thing* thing, uint32_t count,
uint32_t flags) const
{
if(((flags & FLAG_CHILDISOWNER) == FLAG_CHILDISOWNER))
{
//a child container is querying, since we are the top container (not carried by a player)
//just return with no error.
return RET_NOERROR;
}
const Item* item = thing->getItem();
if(!item)
return RET_NOTPOSSIBLE;
if(!item->isPickupable())
return RET_CANNOTPICKUP;
if(item == this)
return RET_THISISIMPOSSIBLE;
const Cylinder* cylinder = getParent();
while(cylinder)
{
if(cylinder == thing)
return RET_THISISIMPOSSIBLE;
cylinder = cylinder->getParent();
}
if(index == INDEX_WHEREEVER && !((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT) && full())
return RET_CONTAINERNOTENOUGHROOM;
const Cylinder* topParent = getTopParent();
if(topParent != this)
return topParent->__queryAdd(INDEX_WHEREEVER, item, count, flags | FLAG_CHILDISOWNER);
return RET_NOERROR;
}
ReturnValue Container::__queryMaxCount(int32_t index, const Thing* thing, uint32_t count,
uint32_t& maxQueryCount, uint32_t flags) const
{
const Item* item = thing->getItem();
if(!item)
{
maxQueryCount = 0;
return RET_NOTPOSSIBLE;
}
if(((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT))
{
maxQueryCount = std::max((uint32_t)1, count);
return RET_NOERROR;
}
int32_t freeSlots = std::max((int32_t)(capacity() - size()), (int32_t)0);
if(item->isStackable())
{
uint32_t n = 0;
if(index != INDEX_WHEREEVER)
{
const Thing* destThing = __getThing(index);
const Item* destItem = NULL;
if(destThing)
destItem = destThing->getItem();
if(destItem && destItem->getID() == item->getID())
n = 100 - destItem->getItemCount();
}
maxQueryCount = freeSlots * 100 + n;
if(maxQueryCount < count)
return RET_CONTAINERNOTENOUGHROOM;
}
else
{
maxQueryCount = freeSlots;
if(maxQueryCount == 0)
return RET_CONTAINERNOTENOUGHROOM;
}
return RET_NOERROR;
}
ReturnValue Container::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const
{
int32_t index = __getIndexOfThing(thing);
if(index == -1)
return RET_NOTPOSSIBLE;
const Item* item = thing->getItem();
if(item == NULL)
return RET_NOTPOSSIBLE;
if(count == 0 || (item->isStackable() && count > item->getItemCount()))
return RET_NOTPOSSIBLE;
if(item->isNotMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags))
return RET_NOTMOVEABLE;
return RET_NOERROR;
}
Cylinder* Container::__queryDestination(int32_t& index, const Thing* thing, Item** destItem,
uint32_t& flags)
{
if(index == 254 /*move up*/)
{
index = INDEX_WHEREEVER;
*destItem = NULL;
Container* parentContainer = dynamic_cast(getParent());
if(parentContainer)
return parentContainer;
else
return this;
}
else if(index == 255 /*add wherever*/)
{
index = INDEX_WHEREEVER;
*destItem = NULL;
return this;
}
else
{
if(index >= (int32_t)capacity())
{
/*
if you have a container, maximize it to show all 20 slots
then you open a bag that is inside the container you will have a bag with 8 slots
and a "grey" area where the other 12 slots where from the container
if you drop the item on that grey area
the client calculates the slot position as if the bag has 20 slots
*/
index = INDEX_WHEREEVER;
}
if(index != INDEX_WHEREEVER)
{
Thing* destThing = __getThing(index);
if(destThing)
*destItem = destThing->getItem();
if(Cylinder* subCylinder = dynamic_cast(*destItem))
{
index = INDEX_WHEREEVER;
*destItem = NULL;
return subCylinder;
}
}
}
return this;
}
void Container::__addThing(Creature* actor, Thing* thing)
{
return __addThing(actor, 0, thing);
}
void Container::__addThing(Creature* actor, int32_t index, Thing* thing)
{
if(index >= (int32_t)capacity())
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__addThing], index:" << index << ", index >= capacity()" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
Item* item = thing->getItem();
if(item == NULL)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__addThing] item == NULL" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
#ifdef __DEBUG__MOVESYS__
if(index != INDEX_WHEREEVER)
{
if(size() >= capacity())
{
std::cout << "Failure: [Container::__addThing] size() >= capacity()" << std::endl;
DEBUG_REPORT
return /*RET_CONTAINERNOTENOUGHROOM*/;
}
}
#endif
item->setParent(this);
itemlist.push_front(item);
totalWeight += item->getWeight();
if(Container* parentContainer = getParentContainer())
parentContainer->updateItemWeight(item->getWeight());
//send change to client
if(getParent())
onAddContainerItem(item);
}
void Container::__updateThing(Thing* thing, uint16_t itemId, uint32_t count)
{
int32_t index = __getIndexOfThing(thing);
if(index == -1)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__updateThing] index == -1" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
Item* item = thing->getItem();
if(item == NULL)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__updateThing] item == NULL" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
const ItemType& oldType = Item::items[item->getID()];
const ItemType& newType = Item::items[itemId];
const double oldWeight = item->getWeight();
item->setID(itemId);
item->setSubType(count);
const double diffWeight = -oldWeight + item->getWeight();
totalWeight += diffWeight;
if(Container* parentContainer = getParentContainer())
parentContainer->updateItemWeight(diffWeight);
//send change to client
if(getParent())
onUpdateContainerItem(index, item, oldType, item, newType);
}
void Container::__replaceThing(uint32_t index, Thing* thing)
{
Item* item = thing->getItem();
if(item == NULL)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__replaceThing] item == NULL" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
uint32_t count = 0;
ItemList::iterator cit = itemlist.end();
for(cit = itemlist.begin(); cit != itemlist.end(); ++cit)
{
if(count == index)
break;
else
++count;
}
if(cit == itemlist.end())
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__updateThing] item not found" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
totalWeight -= (*cit)->getWeight();
totalWeight += item->getWeight();
if(Container* parentContainer = getParentContainer())
parentContainer->updateItemWeight(-(*cit)->getWeight() + item->getWeight());
itemlist.insert(cit, item);
item->setParent(this);
//send change to client
if(getParent())
{
const ItemType& oldType = Item::items[(*cit)->getID()];
const ItemType& newType = Item::items[item->getID()];
onUpdateContainerItem(index, *cit, oldType, item, newType);
}
(*cit)->setParent(NULL);
itemlist.erase(cit);
}
void Container::__removeThing(Thing* thing, uint32_t count)
{
Item* item = thing->getItem();
if(item == NULL)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__removeThing] item == NULL" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
int32_t index = __getIndexOfThing(thing);
if(index == -1)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__removeThing] index == -1" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
ItemList::iterator cit = std::find(itemlist.begin(), itemlist.end(), thing);
if(cit == itemlist.end())
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__removeThing] item not found" << std::endl;
DEBUG_REPORT
#endif
return /*RET_NOTPOSSIBLE*/;
}
if(item->isStackable() && count != item->getItemCount())
{
const double oldWeight = -item->getWeight();
item->setItemCount(std::max(0, (int32_t)(item->getItemCount() - count)));
const double diffWeight = oldWeight + item->getWeight();
totalWeight += diffWeight;
//send change to client
if(getParent())
{
if(Container* parentContainer = getParentContainer())
parentContainer->updateItemWeight(diffWeight);
const ItemType& it = Item::items[item->getID()];
onUpdateContainerItem(index, item, it, item, it);
}
}
else
{
//send change to client
if(getParent())
{
if(Container* parentContainer = getParentContainer())
parentContainer->updateItemWeight(-item->getWeight());
onRemoveContainerItem(index, item);
}
totalWeight -= item->getWeight();
item->setParent(NULL);
itemlist.erase(cit);
}
}
int32_t Container::__getIndexOfThing(const Thing* thing) const
{
uint32_t index = 0;
for(ItemList::const_iterator cit = getItems(); cit != getEnd(); ++cit)
{
if(*cit == thing)
return index;
else
++index;
}
return -1;
}
int32_t Container::__getFirstIndex() const
{
return 0;
}
int32_t Container::__getLastIndex() const
{
return size();
}
uint32_t Container::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, bool itemCount /*= true*/) const
{
uint32_t count = 0;
Item* item = NULL;
for(ItemList::const_iterator it = itemlist.begin(); it != itemlist.end(); ++it)
{
item = (*it);
if(item && item->getID() == itemId && (subType == -1 || subType == item->getSubType()))
{
if(!itemCount)
{
if(item->isRune())
count += item->getCharges();
else
count += item->getItemCount();
}
else
count += item->getItemCount();
}
}
return count;
}
Thing* Container::__getThing(uint32_t index) const
{
if(index < 0 || index > size())
return NULL;
uint32_t count = 0;
for(ItemList::const_iterator cit = itemlist.begin(); cit != itemlist.end(); ++cit)
{
if(count == index)
return *cit;
else
++count;
}
return NULL;
}
void Container::postAddNotification(Creature* actor, Thing* thing, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
{
Cylinder* topParent = getTopParent();
if(!topParent->getCreature())
{
if(topParent == this)
{
//let the tile class notify surrounding players
if(topParent->getParent())
topParent->getParent()->postAddNotification(actor, thing, index, LINK_NEAR);
}
else
topParent->postAddNotification(actor, thing, index, LINK_PARENT);
}
else
topParent->postAddNotification(actor, thing, index, LINK_TOPPARENT);
}
void Container::postRemoveNotification(Creature* actor, Thing* thing, int32_t index, bool isCompleteRemoval, cylinderlink_t link /*= LINK_OWNER*/)
{
Cylinder* topParent = getTopParent();
if(!topParent->getCreature())
{
if(topParent == this)
{
//let the tile class notify surrounding players
if(topParent->getParent())
topParent->getParent()->postRemoveNotification(actor, thing, index, isCompleteRemoval, LINK_NEAR);
}
else
topParent->postRemoveNotification(actor, thing, index, isCompleteRemoval, LINK_PARENT);
}
else
topParent->postRemoveNotification(actor, thing, index, isCompleteRemoval, LINK_TOPPARENT);
}
void Container::__internalAddThing(Thing* thing)
{
__internalAddThing(0, thing);
}
void Container::__internalAddThing(uint32_t index, Thing* thing)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "[Container::__internalAddThing] index: " << index << std::endl;
#endif
if(!thing)
return;
Item* item = thing->getItem();
if(item == NULL)
{
#ifdef __DEBUG__MOVESYS__
std::cout << "Failure: [Container::__internalAddThing] item == NULL" << std::endl;
#endif
return;
}
itemlist.push_front(item);
item->setParent(this);
totalWeight += item->getWeight();
if(Container* parentContainer = getParentContainer())
parentContainer->updateItemWeight(item->getWeight());
}
void Container::__startDecaying()
{
for(ItemList::const_iterator it = itemlist.begin(); it != itemlist.end(); ++it)
(*it)->__startDecaying();
}
ContainerIterator Container::begin()
{
ContainerIterator cit(this);
if(!itemlist.empty())
{
cit.over.push(this);
cit.current = itemlist.begin();
}
return cit;
}
ContainerIterator Container::end()
{
ContainerIterator cit(this);
return cit;
}
ContainerIterator Container::begin() const
{
Container* evil = const_cast(this);
return evil->begin();
}
ContainerIterator Container::end() const
{
Container* evil = const_cast(this);
return evil->end();
}
ContainerIterator::ContainerIterator():
base(NULL) {}
ContainerIterator::ContainerIterator(Container* _base):
base(_base) {}
ContainerIterator::ContainerIterator(const ContainerIterator& rhs):
base(rhs.base), over(rhs.over), current(rhs.current) {}
bool ContainerIterator::operator==(const ContainerIterator& rhs)
{
return !(*this != rhs);
}
bool ContainerIterator::operator!=(const ContainerIterator& rhs)
{
assert(base);
if(base != rhs.base)
return true;
if(over.empty() && rhs.over.empty())
return false;
if(over.empty())
return true;
if(rhs.over.empty())
return true;
if(over.front() != rhs.over.front())
return true;
return current != rhs.current;
}
ContainerIterator& ContainerIterator::operator=(const ContainerIterator& rhs)
{
this->base = rhs.base;
this->current = rhs.current;
this->over = rhs.over;
return *this;
}
Item* ContainerIterator::operator*()
{
assert(base);
return *current;
}
Item* ContainerIterator::operator->()
{
return *(*this);
}
ContainerIterator& ContainerIterator::operator++()
{
assert(base);
if(Item* item = *current)
{
Container* container = item->getContainer();
if(container && !container->empty())
over.push(container);
}
++current;
if(current == over.front()->itemlist.end())
{
over.pop();
if(over.empty())
return *this;
current = over.front()->itemlist.begin();
}
return *this;
}
ContainerIterator ContainerIterator::operator++(int32_t)
{
ContainerIterator tmp(*this);
++*this;
return tmp;
}