]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/inventory.cpp
Decoration API: Allow force_placement of simple decorations
[dragonfireclient.git] / src / inventory.cpp
index ebd0b9c23b471bb579e8aaf27c5083c6e99fb4a6..e89993e63d773f43cd610ee54cb9a1ab26be12db 100644 (file)
@@ -1,25 +1,24 @@
 /*
-Minetest-c55
-Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
+Minetest
+Copyright (C) 2010-2013 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.
 */
 
 #include "inventory.h"
 #include "serialization.h"
-#include "utility.h"
 #include "debug.h"
 #include <sstream>
 #include "log.h"
@@ -27,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "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"
 
 /*
        ItemStack
@@ -125,7 +126,7 @@ ItemStack::ItemStack(std::string name_, u16 count_,
 
 void ItemStack::serialize(std::ostream &os) const
 {
-       DSTACK(__FUNCTION_NAME);
+       DSTACK(FUNCTION_NAME);
 
        if(empty())
                return;
@@ -150,7 +151,7 @@ void ItemStack::serialize(std::ostream &os) const
 
 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
 {
-       DSTACK(__FUNCTION_NAME);
+       DSTACK(FUNCTION_NAME);
 
        clear();
 
@@ -162,7 +163,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
        std::getline(is, tmp, ' ');
        if(!tmp.empty())
                throw SerializationError("Unexpected text after item name");
-       
+
        if(name == "MaterialItem")
        {
                // Obsoleted on 2011-07-30
@@ -174,7 +175,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                // Convert old materials
                if(material <= 0xff)
                        material = content_translate_from_19_to_internal(material);
-               if(material > MAX_CONTENT)
+               if(material > 0xfff)
                        throw SerializationError("Too large material number");
                // Convert old id to name
                NameIdMapping legacy_nimap;
@@ -182,7 +183,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                legacy_nimap.getName(material, name);
                if(name == "")
                        name = "unknown_block";
-               name = itemdef->getAlias(name);
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                count = materialcount;
        }
        else if(name == "MaterialItem2")
@@ -193,7 +195,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                is>>material;
                u16 materialcount;
                is>>materialcount;
-               if(material > MAX_CONTENT)
+               if(material > 0xfff)
                        throw SerializationError("Too large material number");
                // Convert old id to name
                NameIdMapping legacy_nimap;
@@ -201,7 +203,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                legacy_nimap.getName(material, name);
                if(name == "")
                        name = "unknown_block";
-               name = itemdef->getAlias(name);
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                count = materialcount;
        }
        else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
@@ -222,7 +225,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                        name = fnd.next(" ");
                }
                fnd.skip_over(" ");
-               name = itemdef->getAlias(name);
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                count = stoi(trim(fnd.next("")));
                if(count == 0)
                        count = 1;
@@ -251,6 +255,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                count = 1;
                // Then read wear
                fnd.skip_over(" ");
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                wear = stoi(trim(fnd.next("")));
        }
        else
@@ -260,7 +266,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                        // The real thing
 
                        // Apply item aliases
-                       name = itemdef->getAlias(name);
+                       if (itemdef)
+                               name = itemdef->getAlias(name);
 
                        // Read the count
                        std::string count_str;
@@ -292,9 +299,9 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                } while(false);
        }
 
-       if(name.empty() || count == 0)
+       if (name.empty() || count == 0)
                clear();
-       else if(itemdef->get(name).type == ITEM_TOOL)
+       else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
                count = 1;
 }
 
@@ -306,12 +313,12 @@ void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
 
 std::string ItemStack::getItemString() const
 {
-       // Get item string
        std::ostringstream os(std::ios::binary);
        serialize(os);
        return os.str();
 }
 
+
 ItemStack ItemStack::addItem(const ItemStack &newitem_,
                IItemDefManager *itemdef)
 {
@@ -429,6 +436,7 @@ InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemde
 {
        m_name = name;
        m_size = size;
+       m_width = 0;
        m_itemdef = itemdef;
        clearItems();
        //m_dirty = false;
@@ -457,10 +465,22 @@ void InventoryList::setSize(u32 newsize)
        m_size = newsize;
 }
 
+void InventoryList::setWidth(u32 newwidth)
+{
+       m_width = newwidth;
+}
+
+void InventoryList::setName(const std::string &name)
+{
+       m_name = name;
+}
+
 void InventoryList::serialize(std::ostream &os) const
 {
        //os.imbue(std::locale("C"));
-       
+
+       os<<"Width "<<m_width<<"\n";
+
        for(u32 i=0; i<m_items.size(); i++)
        {
                const ItemStack &item = m_items[i];
@@ -485,6 +505,7 @@ void InventoryList::deSerialize(std::istream &is)
 
        clearItems();
        u32 item_i = 0;
+       m_width = 0;
 
        for(;;)
        {
@@ -506,6 +527,12 @@ void InventoryList::deSerialize(std::istream &is)
                {
                        break;
                }
+               else if(name == "Width")
+               {
+                       iss >> m_width;
+                       if (iss.fail())
+                               throw SerializationError("incorrect width property");
+               }
                else if(name == "Item")
                {
                        if(item_i > getSize() - 1)
@@ -520,10 +547,6 @@ void InventoryList::deSerialize(std::istream &is)
                                throw SerializationError("too many items");
                        m_items[item_i++].clear();
                }
-               else
-               {
-                       throw SerializationError("Unknown inventory identifier");
-               }
        }
 }
 
@@ -536,6 +559,7 @@ InventoryList & InventoryList::operator = (const InventoryList &other)
 {
        m_items = other.m_items;
        m_size = other.m_size;
+       m_width = other.m_width;
        m_name = other.m_name;
        m_itemdef = other.m_itemdef;
        //setDirty(true);
@@ -543,6 +567,26 @@ InventoryList & InventoryList::operator = (const InventoryList &other)
        return *this;
 }
 
+bool InventoryList::operator == (const InventoryList &other) const
+{
+       if(m_size != other.m_size)
+               return false;
+       if(m_width != other.m_width)
+               return false;
+       if(m_name != other.m_name)
+               return false;
+       for(u32 i=0; i<m_items.size(); i++)
+       {
+               ItemStack s1 = m_items[i];
+               ItemStack s2 = other.m_items[i];
+               if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
+                               s1.metadata != s2.metadata)
+                       return false;
+       }
+
+       return true;
+}
+
 const std::string &InventoryList::getName() const
 {
        return m_name;
@@ -553,6 +597,11 @@ u32 InventoryList::getSize() const
        return m_items.size();
 }
 
+u32 InventoryList::getWidth() const
+{
+       return m_width;
+}
+
 u32 InventoryList::getUsedSlots() const
 {
        u32 num = 0;
@@ -571,13 +620,13 @@ u32 InventoryList::getFreeSlots() const
 
 const ItemStack& InventoryList::getItem(u32 i) const
 {
-       assert(i < m_size);
+       assert(i < m_size); // Pre-condition
        return m_items[i];
 }
 
 ItemStack& InventoryList::getItem(u32 i)
 {
-       assert(i < m_size);
+       assert(i < m_size); // Pre-condition
        return m_items[i];
 }
 
@@ -594,7 +643,7 @@ ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
 
 void InventoryList::deleteItem(u32 i)
 {
-       assert(i < m_items.size());
+       assert(i < m_items.size()); // Pre-condition
        m_items[i].clear();
 }
 
@@ -604,7 +653,7 @@ ItemStack InventoryList::addItem(const ItemStack &newitem_)
 
        if(newitem.empty())
                return newitem;
-       
+
        /*
                First try to find if it could be added to some existing items
        */
@@ -681,7 +730,7 @@ bool InventoryList::containsItem(const ItemStack &item) const
                return true;
        for(std::vector<ItemStack>::const_reverse_iterator
                        i = m_items.rbegin();
-                       i != m_items.rend(); i++)
+                       i != m_items.rend(); ++i)
        {
                if(count == 0)
                        break;
@@ -701,7 +750,7 @@ ItemStack InventoryList::removeItem(const ItemStack &item)
        ItemStack removed;
        for(std::vector<ItemStack>::reverse_iterator
                        i = m_items.rbegin();
-                       i != m_items.rend(); i++)
+                       i != m_items.rend(); ++i)
        {
                if(i->name == item.name)
                {
@@ -733,6 +782,89 @@ ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
        return m_items[i].peekItem(peekcount);
 }
 
+void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
+{
+       // 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 dest_size = dest->getSize();
+       // First try all the non-empty slots
+       for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
+               if (!m_items[dest_i].empty()) {
+                       item1 = dest->addItem(dest_i, item1);
+                       if (item1.empty()) return;
+               }
+       }
+
+       // Then try all the empty ones
+       for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
+               if (m_items[dest_i].empty()) {
+                       item1 = dest->addItem(dest_i, item1);
+                       if (item1.empty()) return;
+               }
+       }
+
+       // If we reach this, the item was not fully added
+       // Add the remaining part back to the source item
+       addItem(i, item1);
+}
+
+u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
+               u32 count, bool swap_if_needed, bool *did_swap)
+{
+       if(this == dest && i == dest_i)
+               return count;
+
+       // Take item from source list
+       ItemStack item1;
+       if(count == 0)
+               item1 = changeItem(i, ItemStack());
+       else
+               item1 = takeItem(i, count);
+
+       if(item1.empty())
+               return 0;
+
+       // 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())
+       {
+               // 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 && swap_if_needed) {
+                       // Tell that we swapped
+                       if (did_swap != NULL) {
+                               *did_swap = true;
+                       }
+                       // 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);
+               }
+       }
+       return (oldcount - item1.count);
+}
+
 /*
        Inventory
 */
@@ -744,6 +876,7 @@ Inventory::~Inventory()
 
 void Inventory::clear()
 {
+       m_dirty = true;
        for(u32 i=0; i<m_lists.size(); i++)
        {
                delete m_lists[i];
@@ -751,14 +884,29 @@ void Inventory::clear()
        m_lists.clear();
 }
 
+void Inventory::clearContents()
+{
+       m_dirty = true;
+       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_dirty = false;
        m_itemdef = itemdef;
 }
 
 Inventory::Inventory(const Inventory &other)
 {
        *this = other;
+       m_dirty = false;
 }
 
 Inventory & Inventory::operator = (const Inventory &other)
@@ -766,6 +914,7 @@ Inventory & Inventory::operator = (const Inventory &other)
        // Gracefully handle self assignment
        if(this != &other)
        {
+               m_dirty = true;
                clear();
                m_itemdef = other.m_itemdef;
                for(u32 i=0; i<other.m_lists.size(); i++)
@@ -776,6 +925,19 @@ Inventory & Inventory::operator = (const Inventory &other)
        return *this;
 }
 
+bool Inventory::operator == (const Inventory &other) const
+{
+       if(m_lists.size() != other.m_lists.size())
+               return false;
+
+       for(u32 i=0; i<m_lists.size(); i++)
+       {
+               if(*m_lists[i] != *other.m_lists[i])
+                       return false;
+       }
+       return true;
+}
+
 void Inventory::serialize(std::ostream &os) const
 {
        for(u32 i=0; i<m_lists.size(); i++)
@@ -826,13 +988,14 @@ void Inventory::deSerialize(std::istream &is)
                }
                else
                {
-                       throw SerializationError("Unknown inventory identifier");
+                       throw SerializationError("invalid inventory specifier: " + name);
                }
        }
 }
 
 InventoryList * Inventory::addList(const std::string &name, u32 size)
 {
+       m_dirty = true;
        s32 i = getListIndex(name);
        if(i != -1)
        {
@@ -845,6 +1008,9 @@ InventoryList * Inventory::addList(const std::string &name, u32 size)
        }
        else
        {
+               //don't create list with invalid name
+               if (name.find(" ") != std::string::npos) return NULL;
+
                InventoryList *list = new InventoryList(name, size, m_itemdef);
                m_lists.push_back(list);
                return list;
@@ -859,11 +1025,23 @@ InventoryList * Inventory::getList(const std::string &name)
        return m_lists[i];
 }
 
+std::vector<const InventoryList*> Inventory::getLists()
+{
+       std::vector<const InventoryList*> lists;
+       for(u32 i=0; i<m_lists.size(); i++)
+       {
+               InventoryList *list = m_lists[i];
+               lists.push_back(list);
+       }
+       return lists;
+}
+
 bool Inventory::deleteList(const std::string &name)
 {
        s32 i = getListIndex(name);
        if(i == -1)
                return false;
+       m_dirty = true;
        delete m_lists[i];
        m_lists.erase(m_lists.begin() + i);
        return true;