Noise: Prevent unittest crash caused by division by zero
[oweals/minetest.git] / src / inventory.cpp
index b4d1b4dd931b30ec1ab55905b42ad2a1faeeb4ed..4da380a249b06f58700b3b895b1080c63093c851 100644 (file)
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "itemdef.h"
 #include "util/strfnd.h"
+#include "content_mapnode.h" // For loading legacy MaterialItems
+#include "nameidmapping.h" // For loading legacy MaterialItems
 #include "util/serialize.h"
 #include "util/string.h"
 
@@ -31,6 +33,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        ItemStack
 */
 
+static content_t content_translate_from_19_to_internal(content_t c_from)
+{
+       for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
+       {
+               if(trans_table_19[i][1] == c_from)
+               {
+                       return trans_table_19[i][0];
+               }
+       }
+       return c_from;
+}
+
 ItemStack::ItemStack(const std::string &name_, u16 count_,
                u16 wear_, IItemDefManager *itemdef) :
        name(itemdef->getAlias(name_)),
@@ -85,35 +99,140 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
        if(!tmp.empty())
                throw SerializationError("Unexpected text after item name");
 
-       do { // This loop is just to allow "break;"
-               // The real thing
-
-               // Apply item aliases
+       if(name == "MaterialItem")
+       {
+               // Obsoleted on 2011-07-30
+
+               u16 material;
+               is>>material;
+               u16 materialcount;
+               is>>materialcount;
+               // Convert old materials
+               if(material <= 0xff)
+                       material = content_translate_from_19_to_internal(material);
+               if(material > 0xfff)
+                       throw SerializationError("Too large material number");
+               // Convert old id to name
+               NameIdMapping legacy_nimap;
+               content_mapnode_get_name_id_mapping(&legacy_nimap);
+               legacy_nimap.getName(material, name);
+               if(name == "")
+                       name = "unknown_block";
                if (itemdef)
                        name = itemdef->getAlias(name);
-
-               // Read the count
-               std::string count_str;
-               std::getline(is, count_str, ' ');
-               if (count_str.empty()) {
+               count = materialcount;
+       }
+       else if(name == "MaterialItem2")
+       {
+               // Obsoleted on 2011-11-16
+
+               u16 material;
+               is>>material;
+               u16 materialcount;
+               is>>materialcount;
+               if(material > 0xfff)
+                       throw SerializationError("Too large material number");
+               // Convert old id to name
+               NameIdMapping legacy_nimap;
+               content_mapnode_get_name_id_mapping(&legacy_nimap);
+               legacy_nimap.getName(material, name);
+               if(name == "")
+                       name = "unknown_block";
+               if (itemdef)
+                       name = itemdef->getAlias(name);
+               count = materialcount;
+       }
+       else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
+                       || name == "craft" || name == "CraftItem")
+       {
+               // Obsoleted on 2012-01-07
+
+               std::string all;
+               std::getline(is, all, '\n');
+               // First attempt to read inside ""
+               Strfnd fnd(all);
+               fnd.next("\"");
+               // If didn't skip to end, we have ""s
+               if(!fnd.at_end()){
+                       name = fnd.next("\"");
+               } else { // No luck, just read a word then
+                       fnd.start(all);
+                       name = fnd.next(" ");
+               }
+               fnd.skip_over(" ");
+               if (itemdef)
+                       name = itemdef->getAlias(name);
+               count = stoi(trim(fnd.next("")));
+               if(count == 0)
                        count = 1;
-                       break;
-               } else {
-                       count = stoi(count_str);
+       }
+       else if(name == "MBOItem")
+       {
+               // Obsoleted on 2011-10-14
+               throw SerializationError("MBOItem not supported anymore");
+       }
+       else if(name == "tool" || name == "ToolItem")
+       {
+               // Obsoleted on 2012-01-07
+
+               std::string all;
+               std::getline(is, all, '\n');
+               // First attempt to read inside ""
+               Strfnd fnd(all);
+               fnd.next("\"");
+               // If didn't skip to end, we have ""s
+               if(!fnd.at_end()){
+                       name = fnd.next("\"");
+               } else { // No luck, just read a word then
+                       fnd.start(all);
+                       name = fnd.next(" ");
                }
+               count = 1;
+               // Then read wear
+               fnd.skip_over(" ");
+               if (itemdef)
+                       name = itemdef->getAlias(name);
+               wear = stoi(trim(fnd.next("")));
+       }
+       else
+       {
+               do  // This loop is just to allow "break;"
+               {
+                       // The real thing
+
+                       // Apply item aliases
+                       if (itemdef)
+                               name = itemdef->getAlias(name);
+
+                       // Read the count
+                       std::string count_str;
+                       std::getline(is, count_str, ' ');
+                       if(count_str.empty())
+                       {
+                               count = 1;
+                               break;
+                       }
+                       else
+                               count = stoi(count_str);
 
-               // Read the wear
-               std::string wear_str;
-               std::getline(is, wear_str, ' ');
-               if (wear_str.empty())
-                       break;
-               else
-                       wear = stoi(wear_str);
+                       // Read the wear
+                       std::string wear_str;
+                       std::getline(is, wear_str, ' ');
+                       if(wear_str.empty())
+                               break;
+                       else
+                               wear = stoi(wear_str);
 
-               // Read metadata
-               metadata.deSerialize(is);
+                       // Read metadata
+                       metadata.deSerialize(is);
 
-       } while(false);
+                       // In case fields are added after metadata, skip space here:
+                       //std::getline(is, tmp, ' ');
+                       //if(!tmp.empty())
+                       //      throw SerializationError("Unexpected text after metadata");
+
+               } while(false);
+       }
 
        if (name.empty() || count == 0)
                clear();
@@ -135,11 +254,8 @@ std::string ItemStack::getItemString() const
 }
 
 
-ItemStack ItemStack::addItem(const ItemStack &newitem_,
-               IItemDefManager *itemdef)
+ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
 {
-       ItemStack newitem = newitem_;
-
        // If the item is empty or the position invalid, bail out
        if(newitem.empty())
        {
@@ -148,8 +264,17 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_,
        // If this is an empty item, it's an easy job.
        else if(empty())
        {
+               const u16 stackMax = newitem.getStackMax(itemdef);
+
                *this = newitem;
-               newitem.clear();
+
+               // If the item fits fully, delete it
+               if (count <= stackMax) {
+                       newitem.clear();
+               } else { // Else the item does not fit fully. Return the rest.
+                       count = stackMax;
+                       newitem.remove(count);
+               }
        }
        // If item name or metadata differs, bail out
        else if (name != newitem.name
@@ -175,11 +300,10 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_,
        return newitem;
 }
 
-bool ItemStack::itemFits(const ItemStack &newitem_,
+bool ItemStack::itemFits(ItemStack newitem,
                ItemStack *restitem,
                IItemDefManager *itemdef) const
 {
-       ItemStack newitem = newitem_;
 
        // If the item is empty or the position invalid, bail out
        if(newitem.empty())
@@ -189,7 +313,14 @@ bool ItemStack::itemFits(const ItemStack &newitem_,
        // If this is an empty item, it's an easy job.
        else if(empty())
        {
-               newitem.clear();
+               const u16 stackMax = newitem.getStackMax(itemdef);
+
+               // If the item fits fully, delete it
+               if (newitem.count <= stackMax) {
+                       newitem.clear();
+               } else { // Else the item does not fit fully. Return the rest.
+                       newitem.remove(stackMax);
+               }
        }
        // If item name or metadata differs, bail out
        else if (name != newitem.name
@@ -203,7 +334,6 @@ bool ItemStack::itemFits(const ItemStack &newitem_,
                newitem.clear();
        }
        // Else the item does not fit fully. Return the rest.
-       // the rest.
        else
        {
                u16 freespace = freeSpace(itemdef);
@@ -253,7 +383,6 @@ ItemStack ItemStack::peekItem(u32 peekcount) const
 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
        m_name(name),
        m_size(size),
-       m_width(0),
        m_itemdef(itemdef)
 {
        clearItems();
@@ -540,7 +669,7 @@ bool InventoryList::roomForItem(const ItemStack &item_) const
        return false;
 }
 
-bool InventoryList::containsItem(const ItemStack &item) const
+bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
 {
        u32 count = item.count;
        if(count == 0)
@@ -551,9 +680,9 @@ bool InventoryList::containsItem(const ItemStack &item) const
        {
                if(count == 0)
                        break;
-               if(i->name == item.name)
-               {
-                       if(i->count >= count)
+               if (i->name == item.name
+                               && (!match_meta || (i->metadata == item.metadata))) {
+                       if (i->count >= count)
                                return true;
                        else
                                count -= i->count;