]> git.lizzy.rs Git - minetest.git/blobdiff - src/inventory.cpp
Switch the license to be LGPLv2/later, with small parts still remaining as GPLv2...
[minetest.git] / src / inventory.cpp
index 073477dde4cc9a2f08323a6f5b52baa3660b5697..f4a4f280863133566fc4f6f6b7e1dfa201557fb8 100644 (file)
 /*
 Minetest-c55
-Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 
 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 2 of the License, or
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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.
+GNU Lesser General Public License for more details.
 
-You should have received a copy of the GNU General Public License along
+You should have received a copy of the GNU Lesser General Public License along
 with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-/*
-(c) 2010 Perttu Ahola <celeron55@gmail.com>
-*/
-
 #include "inventory.h"
 #include "serialization.h"
 #include "utility.h"
 #include "debug.h"
 #include <sstream>
-#include "main.h"
+#include "log.h"
+#include "itemdef.h"
+#include "strfnd.h"
+#include "content_mapnode.h" // For loading legacy MaterialItems
+#include "nameidmapping.h" // For loading legacy MaterialItems
 
 /*
-       InventoryItem
+       ItemStack
 */
 
-InventoryItem::InventoryItem()
+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;
 }
 
-InventoryItem::~InventoryItem()
+// If the string contains spaces, quotes or control characters, encodes as JSON.
+// Else returns the string unmodified.
+static std::string serializeJsonStringIfNeeded(const std::string &s)
 {
+       for(size_t i = 0; i < s.size(); ++i)
+       {
+               if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
+                       return serializeJsonString(s);
+       }
+       return s;
+}
+
+// Parses a string serialized by serializeJsonStringIfNeeded.
+static std::string deSerializeJsonStringIfNeeded(std::istream &is)
+{
+       std::ostringstream tmp_os;
+       bool expect_initial_quote = true;
+       bool is_json = false;
+       bool was_backslash = false;
+       for(;;)
+       {
+               char c = is.get();
+               if(is.eof())
+                       break;
+               if(expect_initial_quote && c == '"')
+               {
+                       tmp_os << c;
+                       is_json = true;
+               }
+               else if(is_json)
+               {
+                       tmp_os << c;
+                       if(was_backslash)
+                               was_backslash = false;
+                       else if(c == '\\')
+                               was_backslash = true;
+                       else if(c == '"')
+                               break; // Found end of string
+               }
+               else
+               {
+                       if(c == ' ')
+                       {
+                               // Found end of word
+                               is.unget();
+                               break;
+                       }
+                       else
+                       {
+                               tmp_os << c;
+                       }
+               }
+               expect_initial_quote = false;
+       }
+       if(is_json)
+       {
+               std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
+               return deSerializeJsonString(tmp_is);
+       }
+       else
+               return tmp_os.str();
 }
 
-InventoryItem* InventoryItem::deSerialize(std::istream &is)
+
+ItemStack::ItemStack(std::string name_, u16 count_,
+               u16 wear_, std::string metadata_,
+               IItemDefManager *itemdef)
+{
+       name = itemdef->getAlias(name_);
+       count = count_;
+       wear = wear_;
+       metadata = metadata_;
+
+       if(name.empty() || count == 0)
+               clear();
+       else if(itemdef->get(name).type == ITEM_TOOL)
+               count = 1;
+}
+
+void ItemStack::serialize(std::ostream &os) const
 {
        DSTACK(__FUNCTION_NAME);
 
-       //is.imbue(std::locale("C"));
+       if(empty())
+               return;
+
+       // Check how many parts of the itemstring are needed
+       int parts = 1;
+       if(count != 1)
+               parts = 2;
+       if(wear != 0)
+               parts = 3;
+       if(metadata != "")
+               parts = 4;
+
+       os<<serializeJsonStringIfNeeded(name);
+       if(parts >= 2)
+               os<<" "<<count;
+       if(parts >= 3)
+               os<<" "<<wear;
+       if(parts >= 4)
+               os<<" "<<serializeJsonStringIfNeeded(metadata);
+}
+
+void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       clear();
+
        // Read name
-       std::string name;
-       std::getline(is, name, ' ');
+       name = deSerializeJsonStringIfNeeded(is);
+
+       // Skip space
+       std::string tmp;
+       std::getline(is, tmp, ' ');
+       if(!tmp.empty())
+               throw SerializationError("Unexpected text after item name");
        
        if(name == "MaterialItem")
        {
-               // u16 reads directly as a number (u8 doesn't)
+               // Obsoleted on 2011-07-30
+
                u16 material;
                is>>material;
-               u16 count;
-               is>>count;
-               if(material > 255)
+               u16 materialcount;
+               is>>materialcount;
+               // Convert old materials
+               if(material <= 0xff)
+                       material = content_translate_from_19_to_internal(material);
+               if(material > MAX_CONTENT)
                        throw SerializationError("Too large material number");
-               return new MaterialItem(material, count);
+               // 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";
+               name = itemdef->getAlias(name);
+               count = materialcount;
+       }
+       else if(name == "MaterialItem2")
+       {
+               // Obsoleted on 2011-11-16
+
+               u16 material;
+               is>>material;
+               u16 materialcount;
+               is>>materialcount;
+               if(material > MAX_CONTENT)
+                       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";
+               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.atend()){
+                       name = fnd.next("\"");
+               } else { // No luck, just read a word then
+                       fnd.start(all);
+                       name = fnd.next(" ");
+               }
+               fnd.skip_over(" ");
+               name = itemdef->getAlias(name);
+               count = stoi(trim(fnd.next("")));
+               if(count == 0)
+                       count = 1;
        }
        else if(name == "MBOItem")
        {
-               std::string inventorystring;
-               std::getline(is, inventorystring, '|');
-               return new MapBlockObjectItem(inventorystring);
+               // 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.atend()){
+                       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(" ");
+               name = itemdef->getAlias(name);
+               wear = stoi(trim(fnd.next("")));
        }
        else
        {
-               dstream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
-               throw SerializationError("Unknown InventoryItem name");
+               do  // This loop is just to allow "break;"
+               {
+                       // The real thing
+
+                       // Apply item aliases
+                       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 metadata
+                       metadata = deSerializeJsonStringIfNeeded(is);
+
+                       // 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();
+       else if(itemdef->get(name).type == ITEM_TOOL)
+               count = 1;
 }
 
-/*
-       MapBlockObjectItem
-*/
-#ifndef SERVER
-video::ITexture * MapBlockObjectItem::getImage()
+void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
 {
-       if(m_inventorystring.substr(0,3) == "Rat")
-               //return g_device->getVideoDriver()->getTexture("../data/rat.png");
-               return g_irrlicht->getTexture("../data/rat.png");
-       
-       if(m_inventorystring.substr(0,4) == "Sign")
-               //return g_device->getVideoDriver()->getTexture("../data/sign.png");
-               return g_irrlicht->getTexture("../data/sign.png");
+       std::istringstream is(str, std::ios::binary);
+       deSerialize(is, itemdef);
+}
 
-       return NULL;
+std::string ItemStack::getItemString() const
+{
+       // Get item string
+       std::ostringstream os(std::ios::binary);
+       serialize(os);
+       return os.str();
 }
-#endif
-std::string MapBlockObjectItem::getText()
+
+ItemStack ItemStack::addItem(const ItemStack &newitem_,
+               IItemDefManager *itemdef)
 {
-       if(m_inventorystring.substr(0,3) == "Rat")
-               return "";
-       
-       if(m_inventorystring.substr(0,4) == "Sign")
-               return "";
+       ItemStack newitem = newitem_;
 
-       return "obj";
+       // If the item is empty or the position invalid, bail out
+       if(newitem.empty())
+       {
+               // nothing can be added trivially
+       }
+       // If this is an empty item, it's an easy job.
+       else if(empty())
+       {
+               *this = newitem;
+               newitem.clear();
+       }
+       // If item name differs, bail out
+       else if(name != newitem.name)
+       {
+               // cannot be added
+       }
+       // If the item fits fully, add counter and delete it
+       else if(newitem.count <= freeSpace(itemdef))
+       {
+               add(newitem.count);
+               newitem.clear();
+       }
+       // Else the item does not fit fully. Add all that fits and return
+       // the rest.
+       else
+       {
+               u16 freespace = freeSpace(itemdef);
+               add(freespace);
+               newitem.remove(freespace);
+       }
+
+       return newitem;
 }
 
-MapBlockObject * MapBlockObjectItem::createObject
-               (v3f pos, f32 player_yaw, f32 player_pitch)
+bool ItemStack::itemFits(const ItemStack &newitem_,
+               ItemStack *restitem,
+               IItemDefManager *itemdef) const
 {
-       std::istringstream is(m_inventorystring);
-       std::string name;
-       std::getline(is, name, ' ');
-       
-       if(name == "None")
+       ItemStack newitem = newitem_;
+
+       // If the item is empty or the position invalid, bail out
+       if(newitem.empty())
        {
-               return NULL;
+               // nothing can be added trivially
+       }
+       // If this is an empty item, it's an easy job.
+       else if(empty())
+       {
+               newitem.clear();
        }
-       else if(name == "Sign")
+       // If item name differs, bail out
+       else if(name != newitem.name)
        {
-               std::string text;
-               std::getline(is, text, '|');
-               SignObject *obj = new SignObject(NULL, -1, pos);
-               obj->setText(text);
-               obj->setYaw(-player_yaw);
-               return obj;
+               // cannot be added
        }
-       else if(name == "Rat")
+       // If the item fits fully, delete it
+       else if(newitem.count <= freeSpace(itemdef))
        {
-               RatObject *obj = new RatObject(NULL, -1, pos);
-               return obj;
+               newitem.clear();
        }
+       // Else the item does not fit fully. Return the rest.
+       // the rest.
        else
        {
-               return NULL;
+               u16 freespace = freeSpace(itemdef);
+               newitem.remove(freespace);
+       }
+
+       if(restitem)
+               *restitem = newitem;
+       return newitem.empty();
+}
+
+ItemStack ItemStack::takeItem(u32 takecount)
+{
+       if(takecount == 0 || count == 0)
+               return ItemStack();
+
+       ItemStack result = *this;
+       if(takecount >= count)
+       {
+               // Take all
+               clear();
        }
+       else
+       {
+               // Take part
+               remove(takecount);
+               result.count = takecount;
+       }
+       return result;
+}
+
+ItemStack ItemStack::peekItem(u32 peekcount) const
+{
+       if(peekcount == 0 || count == 0)
+               return ItemStack();
+
+       ItemStack result = *this;
+       if(peekcount < count)
+               result.count = peekcount;
+       return result;
 }
 
 /*
        Inventory
 */
 
-InventoryList::InventoryList(std::string name, u32 size)
+InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef)
 {
        m_name = name;
        m_size = size;
+       m_itemdef = itemdef;
        clearItems();
+       //m_dirty = false;
 }
 
 InventoryList::~InventoryList()
 {
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               if(m_items[i])
-                       delete m_items[i];
-       }
 }
 
 void InventoryList::clearItems()
 {
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               if(m_items[i])
-                       delete m_items[i];
-       }
-
        m_items.clear();
 
        for(u32 i=0; i<m_size; i++)
        {
-               m_items.push_back(NULL);
+               m_items.push_back(ItemStack());
        }
+
+       //setDirty(true);
+}
+
+void InventoryList::setSize(u32 newsize)
+{
+       if(newsize != m_items.size())
+               m_items.resize(newsize);
+       m_size = newsize;
+}
+
+void InventoryList::setName(const std::string &name)
+{
+       m_name = name;
 }
 
-void InventoryList::serialize(std::ostream &os)
+void InventoryList::serialize(std::ostream &os) const
 {
        //os.imbue(std::locale("C"));
        
        for(u32 i=0; i<m_items.size(); i++)
        {
-               InventoryItem *item = m_items[i];
-               if(item != NULL)
+               const ItemStack &item = m_items[i];
+               if(item.empty())
                {
-                       os<<"Item ";
-                       item->serialize(os);
+                       os<<"Empty";
                }
                else
                {
-                       os<<"Empty";
+                       os<<"Item ";
+                       item.serialize(os);
                }
                os<<"\n";
        }
 
-       os<<"end\n";
+       os<<"EndInventoryList\n";
 }
 
 void InventoryList::deSerialize(std::istream &is)
@@ -208,7 +503,12 @@ void InventoryList::deSerialize(std::istream &is)
                std::string name;
                std::getline(iss, name, ' ');
 
-               if(name == "end")
+               if(name == "EndInventoryList")
+               {
+                       break;
+               }
+               // This is a temporary backwards compatibility fix
+               else if(name == "end")
                {
                        break;
                }
@@ -216,14 +516,15 @@ void InventoryList::deSerialize(std::istream &is)
                {
                        if(item_i > getSize() - 1)
                                throw SerializationError("too many items");
-                       InventoryItem *item = InventoryItem::deSerialize(iss);
+                       ItemStack item;
+                       item.deSerialize(iss, m_itemdef);
                        m_items[item_i++] = item;
                }
                else if(name == "Empty")
                {
                        if(item_i > getSize() - 1)
                                throw SerializationError("too many items");
-                       m_items[item_i++] = NULL;
+                       m_items[item_i++].clear();
                }
                else
                {
@@ -234,196 +535,249 @@ void InventoryList::deSerialize(std::istream &is)
 
 InventoryList::InventoryList(const InventoryList &other)
 {
-       /*
-               Do this so that the items get cloned. Otherwise the pointers
-               in the array will just get copied.
-       */
        *this = other;
 }
 
 InventoryList & InventoryList::operator = (const InventoryList &other)
 {
-       m_name = other.m_name;
+       m_items = other.m_items;
        m_size = other.m_size;
-       clearItems();
-       for(u32 i=0; i<other.m_items.size(); i++)
-       {
-               InventoryItem *item = other.m_items[i];
-               if(item != NULL)
-               {
-                       m_items[i] = item->clone();
-               }
-       }
+       m_name = other.m_name;
+       m_itemdef = other.m_itemdef;
+       //setDirty(true);
 
        return *this;
 }
 
-std::string InventoryList::getName()
+const std::string &InventoryList::getName() const
 {
        return m_name;
 }
 
-u32 InventoryList::getSize()
+u32 InventoryList::getSize() const
 {
        return m_items.size();
 }
 
-u32 InventoryList::getUsedSlots()
+u32 InventoryList::getUsedSlots() const
 {
        u32 num = 0;
        for(u32 i=0; i<m_items.size(); i++)
        {
-               InventoryItem *item = m_items[i];
-               if(item != NULL)
+               if(!m_items[i].empty())
                        num++;
        }
        return num;
 }
 
-InventoryItem * InventoryList::getItem(u32 i)
+u32 InventoryList::getFreeSlots() const
 {
-       if(i > m_items.size() - 1)
-               return NULL;
+       return getSize() - getUsedSlots();
+}
+
+const ItemStack& InventoryList::getItem(u32 i) const
+{
+       assert(i < m_size);
        return m_items[i];
 }
 
-InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
+ItemStack& InventoryList::getItem(u32 i)
 {
-       assert(i < m_items.size());
+       assert(i < m_size);
+       return m_items[i];
+}
 
-       InventoryItem *olditem = m_items[i];
+ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
+{
+       if(i >= m_items.size())
+               return newitem;
+
+       ItemStack olditem = m_items[i];
        m_items[i] = newitem;
+       //setDirty(true);
        return olditem;
 }
 
 void InventoryList::deleteItem(u32 i)
 {
        assert(i < m_items.size());
-       InventoryItem *item = changeItem(i, NULL);
-       if(item)
-               delete item;
+       m_items[i].clear();
 }
 
-bool InventoryList::addItem(InventoryItem *newitem)
+ItemStack InventoryList::addItem(const ItemStack &newitem_)
 {
-       // If it is a MaterialItem, try to find an already existing one
-       // and just increment the counter
-       if(std::string("MaterialItem") == newitem->getName())
+       ItemStack newitem = newitem_;
+
+       if(newitem.empty())
+               return newitem;
+       
+       /*
+               First try to find if it could be added to some existing items
+       */
+       for(u32 i=0; i<m_items.size(); i++)
        {
-               u8 material = ((MaterialItem*)newitem)->getMaterial();
-               u8 count = ((MaterialItem*)newitem)->getCount();
-               for(u32 i=0; i<m_items.size(); i++)
-               {
-                       InventoryItem *item2 = m_items[i];
-                       if(item2 == NULL)
-                               continue;
-                       if(std::string("MaterialItem") != item2->getName())
-                               continue;
-                       // Found one. Check if it is of the right material and has
-                       // free space
-                       MaterialItem *mitem2 = (MaterialItem*)item2;
-                       if(mitem2->getMaterial() != material)
-                               continue;
-                       //TODO: Add all that can be added and add remaining part
-                       // to another place
-                       if(mitem2->freeSpace() < count)
-                               continue;
-                       // Add to the counter
-                       mitem2->add(count);
-                       // Dump the parameter
-                       delete newitem;
-                       return true;
-               }
+               // Ignore empty slots
+               if(m_items[i].empty())
+                       continue;
+               // Try adding
+               newitem = addItem(i, newitem);
+               if(newitem.empty())
+                       return newitem; // All was eaten
        }
-       // Else find an empty position
+
+       /*
+               Then try to add it to empty slots
+       */
        for(u32 i=0; i<m_items.size(); i++)
        {
-               InventoryItem *item = m_items[i];
-               if(item != NULL)
+               // Ignore unempty slots
+               if(!m_items[i].empty())
                        continue;
-               m_items[i] = newitem;
-               return true;
+               // Try adding
+               newitem = addItem(i, newitem);
+               if(newitem.empty())
+                       return newitem; // All was eaten
        }
-       // Failed
-       return false;
+
+       // Return leftover
+       return newitem;
+}
+
+ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
+{
+       if(i >= m_items.size())
+               return newitem;
+
+       ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
+       //if(leftover != newitem)
+       //      setDirty(true);
+       return leftover;
 }
 
-bool InventoryList::addItem(u32 i, InventoryItem *newitem)
+bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
+               ItemStack *restitem) const
 {
-       // If it is an empty position, it's an easy job.
-       InventoryItem *item = m_items[i];
-       if(item == NULL)
+       if(i >= m_items.size())
        {
-               m_items[i] = newitem;
-               return true;
+               if(restitem)
+                       *restitem = newitem;
+               return false;
        }
 
-       // If it is a material item, try to 
-       if(std::string("MaterialItem") == newitem->getName())
+       return m_items[i].itemFits(newitem, restitem, m_itemdef);
+}
+
+bool InventoryList::roomForItem(const ItemStack &item_) const
+{
+       ItemStack item = item_;
+       ItemStack leftover;
+       for(u32 i=0; i<m_items.size(); i++)
        {
-               u8 material = ((MaterialItem*)newitem)->getMaterial();
-               u8 count = ((MaterialItem*)newitem)->getCount();
-               InventoryItem *item2 = m_items[i];
+               if(itemFits(i, item, &leftover))
+                       return true;
+               item = leftover;
+       }
+       return false;
+}
 
-               if(item2 != NULL
-                       && std::string("MaterialItem") == item2->getName())
+bool InventoryList::containsItem(const ItemStack &item) const
+{
+       u32 count = item.count;
+       if(count == 0)
+               return true;
+       for(std::vector<ItemStack>::const_reverse_iterator
+                       i = m_items.rbegin();
+                       i != m_items.rend(); i++)
+       {
+               if(count == 0)
+                       break;
+               if(i->name == item.name)
                {
-                       // Check if it is of the right material and has free space
-                       MaterialItem *mitem2 = (MaterialItem*)item2;
-                       if(mitem2->getMaterial() == material
-                                       && mitem2->freeSpace() >= count)
-                       {
-                               // Add to the counter
-                               mitem2->add(count);
-                               // Dump the parameter
-                               delete newitem;
-                               // Done
+                       if(i->count >= count)
                                return true;
-                       }
+                       else
+                               count -= i->count;
                }
        }
-       
        return false;
 }
 
-void InventoryList::decrementMaterials(u16 count)
+ItemStack InventoryList::removeItem(const ItemStack &item)
 {
-       for(u32 i=0; i<m_items.size(); i++)
+       ItemStack removed;
+       for(std::vector<ItemStack>::reverse_iterator
+                       i = m_items.rbegin();
+                       i != m_items.rend(); i++)
        {
-               InventoryItem *item = m_items[i];
-               if(item == NULL)
-                       continue;
-               if(std::string("MaterialItem") == item->getName())
+               if(i->name == item.name)
                {
-                       MaterialItem *mitem = (MaterialItem*)item;
-                       if(mitem->getCount() < count)
-                       {
-                               dstream<<__FUNCTION_NAME<<": decrementMaterials():"
-                                               <<" too small material count"<<std::endl;
-                       }
-                       else if(mitem->getCount() == count)
-                       {
-                               deleteItem(i);
-                       }
-                       else
-                       {
-                               mitem->remove(1);
-                       }
+                       u32 still_to_remove = item.count - removed.count;
+                       removed.addItem(i->takeItem(still_to_remove), m_itemdef);
+                       if(removed.count == item.count)
+                               break;
                }
        }
+       return removed;
 }
 
-void InventoryList::print(std::ostream &o)
+ItemStack InventoryList::takeItem(u32 i, u32 takecount)
 {
-       o<<"InventoryList:"<<std::endl;
-       for(u32 i=0; i<m_items.size(); i++)
+       if(i >= m_items.size())
+               return ItemStack();
+
+       ItemStack taken = m_items[i].takeItem(takecount);
+       //if(!taken.empty())
+       //      setDirty(true);
+       return taken;
+}
+
+ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
+{
+       if(i >= m_items.size())
+               return ItemStack();
+
+       return m_items[i].peekItem(peekcount);
+}
+
+void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
+{
+       if(this == dest && i == dest_i)
+               return;
+
+       // Take item from source list
+       ItemStack item1;
+       if(count == 0)
+               item1 = changeItem(i, ItemStack());
+       else
+               item1 = takeItem(i, count);
+
+       if(item1.empty())
+               return;
+
+       // Try to add the item to destination list
+       u32 oldcount = item1.count;
+       item1 = dest->addItem(dest_i, item1);
+
+       // If something is returned, the item was not fully added
+       if(!item1.empty())
        {
-               InventoryItem *item = m_items[i];
-               if(item != NULL)
+               // If olditem is returned, nothing was added.
+               bool nothing_added = (item1.count == oldcount);
+
+               // If something else is returned, part of the item was left unadded.
+               // Add the other part back to the source item
+               addItem(i, item1);
+
+               // If olditem is returned, nothing was added.
+               // Swap the items
+               if(nothing_added)
                {
-                       o<<i<<": ";
-                       item->serialize(o);
-                       o<<"\n";
+                       // Take item from source list
+                       item1 = changeItem(i, ItemStack());
+                       // Adding was not possible, swap the items.
+                       ItemStack item2 = dest->changeItem(dest_i, item1);
+                       // Put item from destination list to the source list
+                       changeItem(i, item2);
                }
        }
 }
@@ -446,8 +800,21 @@ void Inventory::clear()
        m_lists.clear();
 }
 
-Inventory::Inventory()
+void Inventory::clearContents()
+{
+       for(u32 i=0; i<m_lists.size(); i++)
+       {
+               InventoryList *list = m_lists[i];
+               for(u32 j=0; j<list->getSize(); j++)
+               {
+                       list->deleteItem(j);
+               }
+       }
+}
+
+Inventory::Inventory(IItemDefManager *itemdef)
 {
+       m_itemdef = itemdef;
 }
 
 Inventory::Inventory(const Inventory &other)
@@ -457,15 +824,20 @@ Inventory::Inventory(const Inventory &other)
 
 Inventory & Inventory::operator = (const Inventory &other)
 {
-       clear();
-       for(u32 i=0; i<other.m_lists.size(); i++)
+       // Gracefully handle self assignment
+       if(this != &other)
        {
-               m_lists.push_back(new InventoryList(*other.m_lists[i]));
+               clear();
+               m_itemdef = other.m_itemdef;
+               for(u32 i=0; i<other.m_lists.size(); i++)
+               {
+                       m_lists.push_back(new InventoryList(*other.m_lists[i]));
+               }
        }
        return *this;
 }
 
-void Inventory::serialize(std::ostream &os)
+void Inventory::serialize(std::ostream &os) const
 {
        for(u32 i=0; i<m_lists.size(); i++)
        {
@@ -474,7 +846,7 @@ void Inventory::serialize(std::ostream &os)
                list->serialize(os);
        }
 
-       os<<"end\n";
+       os<<"EndInventory\n";
 }
 
 void Inventory::deSerialize(std::istream &is)
@@ -491,7 +863,12 @@ void Inventory::deSerialize(std::istream &is)
                std::string name;
                std::getline(iss, name, ' ');
 
-               if(name == "end")
+               if(name == "EndInventory")
+               {
+                       break;
+               }
+               // This is a temporary backwards compatibility fix
+               else if(name == "end")
                {
                        break;
                }
@@ -503,7 +880,7 @@ void Inventory::deSerialize(std::istream &is)
                        std::getline(iss, listname, ' ');
                        iss>>listsize;
 
-                       InventoryList *list = new InventoryList(listname, listsize);
+                       InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
                        list->deSerialize(is);
 
                        m_lists.push_back(list);
@@ -523,14 +900,15 @@ InventoryList * Inventory::addList(const std::string &name, u32 size)
                if(m_lists[i]->getSize() != size)
                {
                        delete m_lists[i];
-                       m_lists[i] = new InventoryList(name, size);
+                       m_lists[i] = new InventoryList(name, size, m_itemdef);
                }
                return m_lists[i];
        }
        else
        {
-               m_lists.push_back(new InventoryList(name, size));
-               return m_lists.getLast();
+               InventoryList *list = new InventoryList(name, size, m_itemdef);
+               m_lists.push_back(list);
+               return list;
        }
 }
 
@@ -542,70 +920,43 @@ InventoryList * Inventory::getList(const std::string &name)
        return m_lists[i];
 }
 
-s32 Inventory::getListIndex(const std::string &name)
+std::vector<const InventoryList*> Inventory::getLists()
 {
+       std::vector<const InventoryList*> lists;
        for(u32 i=0; i<m_lists.size(); i++)
        {
-               if(m_lists[i]->getName() == name)
-                       return i;
+               InventoryList *list = m_lists[i];
+               lists.push_back(list);
        }
-       return -1;
+       return lists;
 }
 
-/*
-       InventoryAction
-*/
-
-InventoryAction * InventoryAction::deSerialize(std::istream &is)
+bool Inventory::deleteList(const std::string &name)
 {
-       std::string type;
-       std::getline(is, type, ' ');
-
-       InventoryAction *a = NULL;
-
-       if(type == "Move")
-       {
-               a = new IMoveAction(is);
-       }
+       s32 i = getListIndex(name);
+       if(i == -1)
+               return false;
+       delete m_lists[i];
+       m_lists.erase(m_lists.begin() + i);
+       return true;
+}
 
-       return a;
+const InventoryList * Inventory::getList(const std::string &name) const
+{
+       s32 i = getListIndex(name);
+       if(i == -1)
+               return NULL;
+       return m_lists[i];
 }
 
-void IMoveAction::apply(Inventory *inventory)
+const s32 Inventory::getListIndex(const std::string &name) const
 {
-       /*dstream<<"from_name="<<from_name<<" to_name="<<to_name<<std::endl;
-       dstream<<"from_i="<<from_i<<" to_i="<<to_i<<std::endl;*/
-       InventoryList *list_from = inventory->getList(from_name);
-       InventoryList *list_to = inventory->getList(to_name);
-       /*dstream<<"list_from="<<list_from<<" list_to="<<list_to
-                       <<std::endl;*/
-       /*if(list_from)
-               dstream<<" list_from->getItem(from_i)="<<list_from->getItem(from_i)
-                               <<std::endl;
-       if(list_to)
-               dstream<<" list_to->getItem(to_i)="<<list_to->getItem(to_i)
-                               <<std::endl;*/
-       
-       if(!list_from || !list_to || list_from->getItem(from_i) == NULL
-                       || (list_from == list_to && from_i == to_i))
-       {
-               dstream<<__FUNCTION_NAME<<": Operation not allowed"<<std::endl;
-               return;
-       }
-       
-       // Take item from source list
-       InventoryItem *item1 = list_from->changeItem(from_i, NULL);
-       // Try to add the item to destination list
-       if(list_to->addItem(to_i, item1))
+       for(u32 i=0; i<m_lists.size(); i++)
        {
-               // Done.
-               return;
+               if(m_lists[i]->getName() == name)
+                       return i;
        }
-       // Adding was not possible, switch it.
-       // Switch it to the destination list
-       InventoryItem *item2 = list_to->changeItem(to_i, item1);
-       // Put item from destination list to the source list
-       list_from->changeItem(from_i, item2);
+       return -1;
 }
-       
+
 //END