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 "content_mapnode.h" // For loading legacy MaterialItems
28 #include "nameidmapping.h" // For loading legacy MaterialItems
29 #include "util/serialize.h"
30 #include "util/string.h"
36 static content_t content_translate_from_19_to_internal(content_t c_from)
38 for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
40 if(trans_table_19[i][1] == c_from)
42 return trans_table_19[i][0];
48 ItemStack::ItemStack(const std::string &name_, u16 count_,
49 u16 wear_, IItemDefManager *itemdef) :
50 name(itemdef->getAlias(name_)),
54 if (name.empty() || count == 0)
56 else if (itemdef->get(name).type == ITEM_TOOL)
60 void ItemStack::serialize(std::ostream &os) const
62 DSTACK(FUNCTION_NAME);
67 // Check how many parts of the itemstring are needed
73 if (!metadata.empty())
76 os<<serializeJsonStringIfNeeded(name);
83 metadata.serialize(os);
87 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
89 DSTACK(FUNCTION_NAME);
94 name = deSerializeJsonStringIfNeeded(is);
98 std::getline(is, tmp, ' ');
100 throw SerializationError("Unexpected text after item name");
102 if(name == "MaterialItem")
104 // Obsoleted on 2011-07-30
110 // Convert old materials
112 material = content_translate_from_19_to_internal(material);
114 throw SerializationError("Too large material number");
115 // Convert old id to name
116 NameIdMapping legacy_nimap;
117 content_mapnode_get_name_id_mapping(&legacy_nimap);
118 legacy_nimap.getName(material, name);
120 name = "unknown_block";
122 name = itemdef->getAlias(name);
123 count = materialcount;
125 else if(name == "MaterialItem2")
127 // Obsoleted on 2011-11-16
134 throw SerializationError("Too large material number");
135 // Convert old id to name
136 NameIdMapping legacy_nimap;
137 content_mapnode_get_name_id_mapping(&legacy_nimap);
138 legacy_nimap.getName(material, name);
140 name = "unknown_block";
142 name = itemdef->getAlias(name);
143 count = materialcount;
145 else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
146 || name == "craft" || name == "CraftItem")
148 // Obsoleted on 2012-01-07
151 std::getline(is, all, '\n');
152 // First attempt to read inside ""
155 // If didn't skip to end, we have ""s
157 name = fnd.next("\"");
158 } else { // No luck, just read a word then
160 name = fnd.next(" ");
164 name = itemdef->getAlias(name);
165 count = stoi(trim(fnd.next("")));
169 else if(name == "MBOItem")
171 // Obsoleted on 2011-10-14
172 throw SerializationError("MBOItem not supported anymore");
174 else if(name == "tool" || name == "ToolItem")
176 // Obsoleted on 2012-01-07
179 std::getline(is, all, '\n');
180 // First attempt to read inside ""
183 // If didn't skip to end, we have ""s
185 name = fnd.next("\"");
186 } else { // No luck, just read a word then
188 name = fnd.next(" ");
194 name = itemdef->getAlias(name);
195 wear = stoi(trim(fnd.next("")));
199 do // This loop is just to allow "break;"
203 // Apply item aliases
205 name = itemdef->getAlias(name);
208 std::string count_str;
209 std::getline(is, count_str, ' ');
210 if(count_str.empty())
216 count = stoi(count_str);
219 std::string wear_str;
220 std::getline(is, wear_str, ' ');
224 wear = stoi(wear_str);
227 metadata.deSerialize(is);
229 // In case fields are added after metadata, skip space here:
230 //std::getline(is, tmp, ' ');
232 // throw SerializationError("Unexpected text after metadata");
237 if (name.empty() || count == 0)
239 else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
243 void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
245 std::istringstream is(str, std::ios::binary);
246 deSerialize(is, itemdef);
249 std::string ItemStack::getItemString() const
251 std::ostringstream os(std::ios::binary);
257 ItemStack ItemStack::addItem(const ItemStack &newitem_,
258 IItemDefManager *itemdef)
260 ItemStack newitem = newitem_;
262 // If the item is empty or the position invalid, bail out
265 // nothing can be added trivially
267 // If this is an empty item, it's an easy job.
270 const u16 stackMax = getStackMax(itemdef);
274 // If the item fits fully, delete it
275 if (count <= stackMax) {
277 } else { // Else the item does not fit fully. Return the rest.
279 newitem.remove(count);
282 // If item name or metadata differs, bail out
283 else if (name != newitem.name
284 || metadata != newitem.metadata)
288 // If the item fits fully, add counter and delete it
289 else if(newitem.count <= freeSpace(itemdef))
294 // Else the item does not fit fully. Add all that fits and return
298 u16 freespace = freeSpace(itemdef);
300 newitem.remove(freespace);
306 bool ItemStack::itemFits(const ItemStack &newitem_,
308 IItemDefManager *itemdef) const
310 ItemStack newitem = newitem_;
312 // If the item is empty or the position invalid, bail out
315 // nothing can be added trivially
317 // If this is an empty item, it's an easy job.
320 const u16 stackMax = getStackMax(itemdef);
322 // If the item fits fully, delete it
323 if (newitem.count <= stackMax) {
325 } else { // Else the item does not fit fully. Return the rest.
326 newitem.remove(stackMax);
329 // If item name or metadata differs, bail out
330 else if (name != newitem.name
331 || metadata != newitem.metadata)
335 // If the item fits fully, delete it
336 else if(newitem.count <= freeSpace(itemdef))
340 // Else the item does not fit fully. Return the rest.
343 u16 freespace = freeSpace(itemdef);
344 newitem.remove(freespace);
349 return newitem.empty();
352 ItemStack ItemStack::takeItem(u32 takecount)
354 if(takecount == 0 || count == 0)
357 ItemStack result = *this;
358 if(takecount >= count)
367 result.count = takecount;
372 ItemStack ItemStack::peekItem(u32 peekcount) const
374 if(peekcount == 0 || count == 0)
377 ItemStack result = *this;
378 if(peekcount < count)
379 result.count = peekcount;
387 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
395 InventoryList::~InventoryList()
399 void InventoryList::clearItems()
403 for(u32 i=0; i<m_size; i++)
405 m_items.push_back(ItemStack());
411 void InventoryList::setSize(u32 newsize)
413 if(newsize != m_items.size())
414 m_items.resize(newsize);
418 void InventoryList::setWidth(u32 newwidth)
423 void InventoryList::setName(const std::string &name)
428 void InventoryList::serialize(std::ostream &os) const
430 //os.imbue(std::locale("C"));
432 os<<"Width "<<m_width<<"\n";
434 for(u32 i=0; i<m_items.size(); i++)
436 const ItemStack &item = m_items[i];
449 os<<"EndInventoryList\n";
452 void InventoryList::deSerialize(std::istream &is)
454 //is.imbue(std::locale("C"));
463 std::getline(is, line, '\n');
465 std::istringstream iss(line);
466 //iss.imbue(std::locale("C"));
469 std::getline(iss, name, ' ');
471 if(name == "EndInventoryList")
475 // This is a temporary backwards compatibility fix
476 else if(name == "end")
480 else if(name == "Width")
484 throw SerializationError("incorrect width property");
486 else if(name == "Item")
488 if(item_i > getSize() - 1)
489 throw SerializationError("too many items");
491 item.deSerialize(iss, m_itemdef);
492 m_items[item_i++] = item;
494 else if(name == "Empty")
496 if(item_i > getSize() - 1)
497 throw SerializationError("too many items");
498 m_items[item_i++].clear();
503 InventoryList::InventoryList(const InventoryList &other)
508 InventoryList & InventoryList::operator = (const InventoryList &other)
510 m_items = other.m_items;
511 m_size = other.m_size;
512 m_width = other.m_width;
513 m_name = other.m_name;
514 m_itemdef = other.m_itemdef;
520 bool InventoryList::operator == (const InventoryList &other) const
522 if(m_size != other.m_size)
524 if(m_width != other.m_width)
526 if(m_name != other.m_name)
528 for(u32 i=0; i<m_items.size(); i++)
530 ItemStack s1 = m_items[i];
531 ItemStack s2 = other.m_items[i];
532 if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
533 s1.metadata != s2.metadata)
540 const std::string &InventoryList::getName() const
545 u32 InventoryList::getSize() const
547 return m_items.size();
550 u32 InventoryList::getWidth() const
555 u32 InventoryList::getUsedSlots() const
558 for(u32 i=0; i<m_items.size(); i++)
560 if(!m_items[i].empty())
566 u32 InventoryList::getFreeSlots() const
568 return getSize() - getUsedSlots();
571 const ItemStack& InventoryList::getItem(u32 i) const
573 assert(i < m_size); // Pre-condition
577 ItemStack& InventoryList::getItem(u32 i)
579 assert(i < m_size); // Pre-condition
583 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
585 if(i >= m_items.size())
588 ItemStack olditem = m_items[i];
589 m_items[i] = newitem;
594 void InventoryList::deleteItem(u32 i)
596 assert(i < m_items.size()); // Pre-condition
600 ItemStack InventoryList::addItem(const ItemStack &newitem_)
602 ItemStack newitem = newitem_;
608 First try to find if it could be added to some existing items
610 for(u32 i=0; i<m_items.size(); i++)
612 // Ignore empty slots
613 if(m_items[i].empty())
616 newitem = addItem(i, newitem);
618 return newitem; // All was eaten
622 Then try to add it to empty slots
624 for(u32 i=0; i<m_items.size(); i++)
626 // Ignore unempty slots
627 if(!m_items[i].empty())
630 newitem = addItem(i, newitem);
632 return newitem; // All was eaten
639 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
641 if(i >= m_items.size())
644 ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
645 //if(leftover != newitem)
650 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
651 ItemStack *restitem) const
653 if(i >= m_items.size())
660 return m_items[i].itemFits(newitem, restitem, m_itemdef);
663 bool InventoryList::roomForItem(const ItemStack &item_) const
665 ItemStack item = item_;
667 for(u32 i=0; i<m_items.size(); i++)
669 if(itemFits(i, item, &leftover))
676 bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
678 u32 count = item.count;
681 for(std::vector<ItemStack>::const_reverse_iterator
682 i = m_items.rbegin();
683 i != m_items.rend(); ++i)
687 if (i->name == item.name
688 && (!match_meta || (i->metadata == item.metadata))) {
689 if (i->count >= count)
698 ItemStack InventoryList::removeItem(const ItemStack &item)
701 for(std::vector<ItemStack>::reverse_iterator
702 i = m_items.rbegin();
703 i != m_items.rend(); ++i)
705 if(i->name == item.name)
707 u32 still_to_remove = item.count - removed.count;
708 removed.addItem(i->takeItem(still_to_remove), m_itemdef);
709 if(removed.count == item.count)
716 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
718 if(i >= m_items.size())
721 ItemStack taken = m_items[i].takeItem(takecount);
727 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
729 // Take item from source list
732 item1 = changeItem(i, ItemStack());
734 item1 = takeItem(i, count);
739 // Try to add the item to destination list
740 u32 dest_size = dest->getSize();
741 // First try all the non-empty slots
742 for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
743 if (!m_items[dest_i].empty()) {
744 item1 = dest->addItem(dest_i, item1);
745 if (item1.empty()) return;
749 // Then try all the empty ones
750 for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
751 if (m_items[dest_i].empty()) {
752 item1 = dest->addItem(dest_i, item1);
753 if (item1.empty()) return;
757 // If we reach this, the item was not fully added
758 // Add the remaining part back to the source item
762 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
763 u32 count, bool swap_if_needed, bool *did_swap)
765 if(this == dest && i == dest_i)
768 // Take item from source list
771 item1 = changeItem(i, ItemStack());
773 item1 = takeItem(i, count);
778 // Try to add the item to destination list
779 u32 oldcount = item1.count;
780 item1 = dest->addItem(dest_i, item1);
782 // If something is returned, the item was not fully added
785 // If olditem is returned, nothing was added.
786 bool nothing_added = (item1.count == oldcount);
788 // If something else is returned, part of the item was left unadded.
789 // Add the other part back to the source item
792 // If olditem is returned, nothing was added.
794 if (nothing_added && swap_if_needed) {
795 // Tell that we swapped
796 if (did_swap != NULL) {
799 // Take item from source list
800 item1 = changeItem(i, ItemStack());
801 // Adding was not possible, swap the items.
802 ItemStack item2 = dest->changeItem(dest_i, item1);
803 // Put item from destination list to the source list
804 changeItem(i, item2);
807 return (oldcount - item1.count);
814 Inventory::~Inventory()
819 void Inventory::clear()
822 for(u32 i=0; i<m_lists.size(); i++)
829 void Inventory::clearContents()
832 for(u32 i=0; i<m_lists.size(); i++)
834 InventoryList *list = m_lists[i];
835 for(u32 j=0; j<list->getSize(); j++)
842 Inventory::Inventory(IItemDefManager *itemdef)
848 Inventory::Inventory(const Inventory &other)
854 Inventory & Inventory::operator = (const Inventory &other)
856 // Gracefully handle self assignment
861 m_itemdef = other.m_itemdef;
862 for(u32 i=0; i<other.m_lists.size(); i++)
864 m_lists.push_back(new InventoryList(*other.m_lists[i]));
870 bool Inventory::operator == (const Inventory &other) const
872 if(m_lists.size() != other.m_lists.size())
875 for(u32 i=0; i<m_lists.size(); i++)
877 if(*m_lists[i] != *other.m_lists[i])
883 void Inventory::serialize(std::ostream &os) const
885 for(u32 i=0; i<m_lists.size(); i++)
887 InventoryList *list = m_lists[i];
888 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
892 os<<"EndInventory\n";
895 void Inventory::deSerialize(std::istream &is)
902 std::getline(is, line, '\n');
904 std::istringstream iss(line);
907 std::getline(iss, name, ' ');
909 if(name == "EndInventory")
913 // This is a temporary backwards compatibility fix
914 else if(name == "end")
918 else if(name == "List")
920 std::string listname;
923 std::getline(iss, listname, ' ');
926 InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
927 list->deSerialize(is);
929 m_lists.push_back(list);
933 throw SerializationError("invalid inventory specifier: " + name);
938 InventoryList * Inventory::addList(const std::string &name, u32 size)
941 s32 i = getListIndex(name);
944 if(m_lists[i]->getSize() != size)
947 m_lists[i] = new InventoryList(name, size, m_itemdef);
953 //don't create list with invalid name
954 if (name.find(" ") != std::string::npos) return NULL;
956 InventoryList *list = new InventoryList(name, size, m_itemdef);
957 m_lists.push_back(list);
962 InventoryList * Inventory::getList(const std::string &name)
964 s32 i = getListIndex(name);
970 std::vector<const InventoryList*> Inventory::getLists()
972 std::vector<const InventoryList*> lists;
973 for(u32 i=0; i<m_lists.size(); i++)
975 InventoryList *list = m_lists[i];
976 lists.push_back(list);
981 bool Inventory::deleteList(const std::string &name)
983 s32 i = getListIndex(name);
988 m_lists.erase(m_lists.begin() + i);
992 const InventoryList * Inventory::getList(const std::string &name) const
994 s32 i = getListIndex(name);
1000 const s32 Inventory::getListIndex(const std::string &name) const
1002 for(u32 i=0; i<m_lists.size(); i++)
1004 if(m_lists[i]->getName() == name)