3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "inventory.h"
21 #include "serialization.h"
26 #include "util/strfnd.h"
27 #include "util/serialize.h"
28 #include "util/string.h"
34 ItemStack::ItemStack(const std::string &name_, u16 count_,
35 u16 wear_, IItemDefManager *itemdef) :
36 name(itemdef->getAlias(name_)),
40 if (name.empty() || count == 0)
42 else if (itemdef->get(name).type == ITEM_TOOL)
46 void ItemStack::serialize(std::ostream &os) const
48 DSTACK(FUNCTION_NAME);
53 // Check how many parts of the itemstring are needed
59 if (!metadata.empty())
62 os<<serializeJsonStringIfNeeded(name);
69 metadata.serialize(os);
73 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
75 DSTACK(FUNCTION_NAME);
80 name = deSerializeJsonStringIfNeeded(is);
84 std::getline(is, tmp, ' ');
86 throw SerializationError("Unexpected text after item name");
88 do { // This loop is just to allow "break;"
93 name = itemdef->getAlias(name);
96 std::string count_str;
97 std::getline(is, count_str, ' ');
98 if (count_str.empty()) {
102 count = stoi(count_str);
106 std::string wear_str;
107 std::getline(is, wear_str, ' ');
108 if (wear_str.empty())
111 wear = stoi(wear_str);
114 metadata.deSerialize(is);
118 if (name.empty() || count == 0)
120 else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
124 void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
126 std::istringstream is(str, std::ios::binary);
127 deSerialize(is, itemdef);
130 std::string ItemStack::getItemString() const
132 std::ostringstream os(std::ios::binary);
138 ItemStack ItemStack::addItem(const ItemStack &newitem_,
139 IItemDefManager *itemdef)
141 ItemStack newitem = newitem_;
143 // If the item is empty or the position invalid, bail out
146 // nothing can be added trivially
148 // If this is an empty item, it's an easy job.
154 // If item name or metadata differs, bail out
155 else if (name != newitem.name
156 || metadata != newitem.metadata)
160 // If the item fits fully, add counter and delete it
161 else if(newitem.count <= freeSpace(itemdef))
166 // Else the item does not fit fully. Add all that fits and return
170 u16 freespace = freeSpace(itemdef);
172 newitem.remove(freespace);
178 bool ItemStack::itemFits(const ItemStack &newitem_,
180 IItemDefManager *itemdef) const
182 ItemStack newitem = newitem_;
184 // If the item is empty or the position invalid, bail out
187 // nothing can be added trivially
189 // If this is an empty item, it's an easy job.
194 // If item name or metadata differs, bail out
195 else if (name != newitem.name
196 || metadata != newitem.metadata)
200 // If the item fits fully, delete it
201 else if(newitem.count <= freeSpace(itemdef))
205 // Else the item does not fit fully. Return the rest.
209 u16 freespace = freeSpace(itemdef);
210 newitem.remove(freespace);
215 return newitem.empty();
218 ItemStack ItemStack::takeItem(u32 takecount)
220 if(takecount == 0 || count == 0)
223 ItemStack result = *this;
224 if(takecount >= count)
233 result.count = takecount;
238 ItemStack ItemStack::peekItem(u32 peekcount) const
240 if(peekcount == 0 || count == 0)
243 ItemStack result = *this;
244 if(peekcount < count)
245 result.count = peekcount;
253 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
262 InventoryList::~InventoryList()
266 void InventoryList::clearItems()
270 for(u32 i=0; i<m_size; i++)
272 m_items.push_back(ItemStack());
278 void InventoryList::setSize(u32 newsize)
280 if(newsize != m_items.size())
281 m_items.resize(newsize);
285 void InventoryList::setWidth(u32 newwidth)
290 void InventoryList::setName(const std::string &name)
295 void InventoryList::serialize(std::ostream &os) const
297 //os.imbue(std::locale("C"));
299 os<<"Width "<<m_width<<"\n";
301 for(u32 i=0; i<m_items.size(); i++)
303 const ItemStack &item = m_items[i];
316 os<<"EndInventoryList\n";
319 void InventoryList::deSerialize(std::istream &is)
321 //is.imbue(std::locale("C"));
330 std::getline(is, line, '\n');
332 std::istringstream iss(line);
333 //iss.imbue(std::locale("C"));
336 std::getline(iss, name, ' ');
338 if(name == "EndInventoryList")
342 // This is a temporary backwards compatibility fix
343 else if(name == "end")
347 else if(name == "Width")
351 throw SerializationError("incorrect width property");
353 else if(name == "Item")
355 if(item_i > getSize() - 1)
356 throw SerializationError("too many items");
358 item.deSerialize(iss, m_itemdef);
359 m_items[item_i++] = item;
361 else if(name == "Empty")
363 if(item_i > getSize() - 1)
364 throw SerializationError("too many items");
365 m_items[item_i++].clear();
370 InventoryList::InventoryList(const InventoryList &other)
375 InventoryList & InventoryList::operator = (const InventoryList &other)
377 m_items = other.m_items;
378 m_size = other.m_size;
379 m_width = other.m_width;
380 m_name = other.m_name;
381 m_itemdef = other.m_itemdef;
387 bool InventoryList::operator == (const InventoryList &other) const
389 if(m_size != other.m_size)
391 if(m_width != other.m_width)
393 if(m_name != other.m_name)
395 for(u32 i=0; i<m_items.size(); i++)
397 ItemStack s1 = m_items[i];
398 ItemStack s2 = other.m_items[i];
399 if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
400 s1.metadata != s2.metadata)
407 const std::string &InventoryList::getName() const
412 u32 InventoryList::getSize() const
414 return m_items.size();
417 u32 InventoryList::getWidth() const
422 u32 InventoryList::getUsedSlots() const
425 for(u32 i=0; i<m_items.size(); i++)
427 if(!m_items[i].empty())
433 u32 InventoryList::getFreeSlots() const
435 return getSize() - getUsedSlots();
438 const ItemStack& InventoryList::getItem(u32 i) const
440 assert(i < m_size); // Pre-condition
444 ItemStack& InventoryList::getItem(u32 i)
446 assert(i < m_size); // Pre-condition
450 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
452 if(i >= m_items.size())
455 ItemStack olditem = m_items[i];
456 m_items[i] = newitem;
461 void InventoryList::deleteItem(u32 i)
463 assert(i < m_items.size()); // Pre-condition
467 ItemStack InventoryList::addItem(const ItemStack &newitem_)
469 ItemStack newitem = newitem_;
475 First try to find if it could be added to some existing items
477 for(u32 i=0; i<m_items.size(); i++)
479 // Ignore empty slots
480 if(m_items[i].empty())
483 newitem = addItem(i, newitem);
485 return newitem; // All was eaten
489 Then try to add it to empty slots
491 for(u32 i=0; i<m_items.size(); i++)
493 // Ignore unempty slots
494 if(!m_items[i].empty())
497 newitem = addItem(i, newitem);
499 return newitem; // All was eaten
506 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
508 if(i >= m_items.size())
511 ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
512 //if(leftover != newitem)
517 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
518 ItemStack *restitem) const
520 if(i >= m_items.size())
527 return m_items[i].itemFits(newitem, restitem, m_itemdef);
530 bool InventoryList::roomForItem(const ItemStack &item_) const
532 ItemStack item = item_;
534 for(u32 i=0; i<m_items.size(); i++)
536 if(itemFits(i, item, &leftover))
543 bool InventoryList::containsItem(const ItemStack &item) const
545 u32 count = item.count;
548 for(std::vector<ItemStack>::const_reverse_iterator
549 i = m_items.rbegin();
550 i != m_items.rend(); ++i)
554 if(i->name == item.name)
556 if(i->count >= count)
565 ItemStack InventoryList::removeItem(const ItemStack &item)
568 for(std::vector<ItemStack>::reverse_iterator
569 i = m_items.rbegin();
570 i != m_items.rend(); ++i)
572 if(i->name == item.name)
574 u32 still_to_remove = item.count - removed.count;
575 removed.addItem(i->takeItem(still_to_remove), m_itemdef);
576 if(removed.count == item.count)
583 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
585 if(i >= m_items.size())
588 ItemStack taken = m_items[i].takeItem(takecount);
594 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
596 // Take item from source list
599 item1 = changeItem(i, ItemStack());
601 item1 = takeItem(i, count);
606 // Try to add the item to destination list
607 u32 dest_size = dest->getSize();
608 // First try all the non-empty slots
609 for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
610 if (!m_items[dest_i].empty()) {
611 item1 = dest->addItem(dest_i, item1);
612 if (item1.empty()) return;
616 // Then try all the empty ones
617 for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
618 if (m_items[dest_i].empty()) {
619 item1 = dest->addItem(dest_i, item1);
620 if (item1.empty()) return;
624 // If we reach this, the item was not fully added
625 // Add the remaining part back to the source item
629 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
630 u32 count, bool swap_if_needed, bool *did_swap)
632 if(this == dest && i == dest_i)
635 // Take item from source list
638 item1 = changeItem(i, ItemStack());
640 item1 = takeItem(i, count);
645 // Try to add the item to destination list
646 u32 oldcount = item1.count;
647 item1 = dest->addItem(dest_i, item1);
649 // If something is returned, the item was not fully added
652 // If olditem is returned, nothing was added.
653 bool nothing_added = (item1.count == oldcount);
655 // If something else is returned, part of the item was left unadded.
656 // Add the other part back to the source item
659 // If olditem is returned, nothing was added.
661 if (nothing_added && swap_if_needed) {
662 // Tell that we swapped
663 if (did_swap != NULL) {
666 // Take item from source list
667 item1 = changeItem(i, ItemStack());
668 // Adding was not possible, swap the items.
669 ItemStack item2 = dest->changeItem(dest_i, item1);
670 // Put item from destination list to the source list
671 changeItem(i, item2);
674 return (oldcount - item1.count);
681 Inventory::~Inventory()
686 void Inventory::clear()
689 for(u32 i=0; i<m_lists.size(); i++)
696 void Inventory::clearContents()
699 for(u32 i=0; i<m_lists.size(); i++)
701 InventoryList *list = m_lists[i];
702 for(u32 j=0; j<list->getSize(); j++)
709 Inventory::Inventory(IItemDefManager *itemdef)
715 Inventory::Inventory(const Inventory &other)
721 Inventory & Inventory::operator = (const Inventory &other)
723 // Gracefully handle self assignment
728 m_itemdef = other.m_itemdef;
729 for(u32 i=0; i<other.m_lists.size(); i++)
731 m_lists.push_back(new InventoryList(*other.m_lists[i]));
737 bool Inventory::operator == (const Inventory &other) const
739 if(m_lists.size() != other.m_lists.size())
742 for(u32 i=0; i<m_lists.size(); i++)
744 if(*m_lists[i] != *other.m_lists[i])
750 void Inventory::serialize(std::ostream &os) const
752 for(u32 i=0; i<m_lists.size(); i++)
754 InventoryList *list = m_lists[i];
755 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
759 os<<"EndInventory\n";
762 void Inventory::deSerialize(std::istream &is)
769 std::getline(is, line, '\n');
771 std::istringstream iss(line);
774 std::getline(iss, name, ' ');
776 if(name == "EndInventory")
780 // This is a temporary backwards compatibility fix
781 else if(name == "end")
785 else if(name == "List")
787 std::string listname;
790 std::getline(iss, listname, ' ');
793 InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
794 list->deSerialize(is);
796 m_lists.push_back(list);
800 throw SerializationError("invalid inventory specifier: " + name);
805 InventoryList * Inventory::addList(const std::string &name, u32 size)
808 s32 i = getListIndex(name);
811 if(m_lists[i]->getSize() != size)
814 m_lists[i] = new InventoryList(name, size, m_itemdef);
820 //don't create list with invalid name
821 if (name.find(" ") != std::string::npos) return NULL;
823 InventoryList *list = new InventoryList(name, size, m_itemdef);
824 m_lists.push_back(list);
829 InventoryList * Inventory::getList(const std::string &name)
831 s32 i = getListIndex(name);
837 std::vector<const InventoryList*> Inventory::getLists()
839 std::vector<const InventoryList*> lists;
840 for(u32 i=0; i<m_lists.size(); i++)
842 InventoryList *list = m_lists[i];
843 lists.push_back(list);
848 bool Inventory::deleteList(const std::string &name)
850 s32 i = getListIndex(name);
855 m_lists.erase(m_lists.begin() + i);
859 const InventoryList * Inventory::getList(const std::string &name) const
861 s32 i = getListIndex(name);
867 const s32 Inventory::getListIndex(const std::string &name) const
869 for(u32 i=0; i<m_lists.size(); i++)
871 if(m_lists[i]->getName() == name)