LCOV - code coverage report
Current view: top level - src - inventory.cpp (source / functions) Hit Total Coverage
Test: report Lines: 214 537 39.9 %
Date: 2015-07-11 18:23:49 Functions: 28 59 47.5 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       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.
       9             : 
      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.
      14             : 
      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.
      18             : */
      19             : 
      20             : #include "inventory.h"
      21             : #include "serialization.h"
      22             : #include "debug.h"
      23             : #include <sstream>
      24             : #include "log.h"
      25             : #include "itemdef.h"
      26             : #include "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"
      31             : 
      32             : /*
      33             :         ItemStack
      34             : */
      35             : 
      36           0 : static content_t content_translate_from_19_to_internal(content_t c_from)
      37             : {
      38           0 :         for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
      39             :         {
      40           0 :                 if(trans_table_19[i][1] == c_from)
      41             :                 {
      42           0 :                         return trans_table_19[i][0];
      43             :                 }
      44             :         }
      45           0 :         return c_from;
      46             : }
      47             : 
      48             : // If the string contains spaces, quotes or control characters, encodes as JSON.
      49             : // Else returns the string unmodified.
      50          55 : static std::string serializeJsonStringIfNeeded(const std::string &s)
      51             : {
      52         858 :         for(size_t i = 0; i < s.size(); ++i)
      53             :         {
      54         803 :                 if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
      55           0 :                         return serializeJsonString(s);
      56             :         }
      57          55 :         return s;
      58             : }
      59             : 
      60             : // Parses a string serialized by serializeJsonStringIfNeeded.
      61        1627 : static std::string deSerializeJsonStringIfNeeded(std::istream &is)
      62             : {
      63        3254 :         std::ostringstream tmp_os;
      64        1627 :         bool expect_initial_quote = true;
      65        1627 :         bool is_json = false;
      66        1627 :         bool was_backslash = false;
      67       32201 :         for(;;)
      68             :         {
      69       33828 :                 char c = is.get();
      70       33828 :                 if(is.eof())
      71        1292 :                         break;
      72       32536 :                 if(expect_initial_quote && c == '"')
      73             :                 {
      74           0 :                         tmp_os << c;
      75           0 :                         is_json = true;
      76             :                 }
      77       32536 :                 else if(is_json)
      78             :                 {
      79           0 :                         tmp_os << c;
      80           0 :                         if(was_backslash)
      81           0 :                                 was_backslash = false;
      82           0 :                         else if(c == '\\')
      83           0 :                                 was_backslash = true;
      84           0 :                         else if(c == '"')
      85           0 :                                 break; // Found end of string
      86             :                 }
      87             :                 else
      88             :                 {
      89       32536 :                         if(c == ' ')
      90             :                         {
      91             :                                 // Found end of word
      92         335 :                                 is.unget();
      93         335 :                                 break;
      94             :                         }
      95             :                         else
      96             :                         {
      97       32201 :                                 tmp_os << c;
      98             :                         }
      99             :                 }
     100       32201 :                 expect_initial_quote = false;
     101             :         }
     102        1627 :         if(is_json)
     103             :         {
     104           0 :                 std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
     105           0 :                 return deSerializeJsonString(tmp_is);
     106             :         }
     107             :         else
     108        1627 :                 return tmp_os.str();
     109             : }
     110             : 
     111             : 
     112          25 : ItemStack::ItemStack(std::string name_, u16 count_,
     113             :                 u16 wear_, std::string metadata_,
     114          25 :                 IItemDefManager *itemdef)
     115             : {
     116          25 :         name = itemdef->getAlias(name_);
     117          25 :         count = count_;
     118          25 :         wear = wear_;
     119          25 :         metadata = metadata_;
     120             : 
     121          25 :         if(name.empty() || count == 0)
     122           0 :                 clear();
     123          25 :         else if(itemdef->get(name).type == ITEM_TOOL)
     124           0 :                 count = 1;
     125          25 : }
     126             : 
     127          55 : void ItemStack::serialize(std::ostream &os) const
     128             : {
     129         110 :         DSTACK(__FUNCTION_NAME);
     130             : 
     131          55 :         if(empty())
     132           0 :                 return;
     133             : 
     134             :         // Check how many parts of the itemstring are needed
     135          55 :         int parts = 1;
     136          55 :         if(count != 1)
     137          44 :                 parts = 2;
     138          55 :         if(wear != 0)
     139           0 :                 parts = 3;
     140          55 :         if(metadata != "")
     141           0 :                 parts = 4;
     142             : 
     143          55 :         os<<serializeJsonStringIfNeeded(name);
     144          55 :         if(parts >= 2)
     145          44 :                 os<<" "<<count;
     146          55 :         if(parts >= 3)
     147           0 :                 os<<" "<<wear;
     148          55 :         if(parts >= 4)
     149           0 :                 os<<" "<<serializeJsonStringIfNeeded(metadata);
     150             : }
     151             : 
     152        1532 : void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
     153             : {
     154        3064 :         DSTACK(__FUNCTION_NAME);
     155             : 
     156        1532 :         clear();
     157             : 
     158             :         // Read name
     159        1532 :         name = deSerializeJsonStringIfNeeded(is);
     160             : 
     161             :         // Skip space
     162        3064 :         std::string tmp;
     163        1532 :         std::getline(is, tmp, ' ');
     164        1532 :         if(!tmp.empty())
     165           0 :                 throw SerializationError("Unexpected text after item name");
     166             :         
     167        1532 :         if(name == "MaterialItem")
     168             :         {
     169             :                 // Obsoleted on 2011-07-30
     170             : 
     171             :                 u16 material;
     172           0 :                 is>>material;
     173             :                 u16 materialcount;
     174           0 :                 is>>materialcount;
     175             :                 // Convert old materials
     176           0 :                 if(material <= 0xff)
     177           0 :                         material = content_translate_from_19_to_internal(material);
     178           0 :                 if(material > 0xfff)
     179           0 :                         throw SerializationError("Too large material number");
     180             :                 // Convert old id to name
     181           0 :                 NameIdMapping legacy_nimap;
     182           0 :                 content_mapnode_get_name_id_mapping(&legacy_nimap);
     183           0 :                 legacy_nimap.getName(material, name);
     184           0 :                 if(name == "")
     185           0 :                         name = "unknown_block";
     186           0 :                 if (itemdef)
     187           0 :                         name = itemdef->getAlias(name);
     188           0 :                 count = materialcount;
     189             :         }
     190        1532 :         else if(name == "MaterialItem2")
     191             :         {
     192             :                 // Obsoleted on 2011-11-16
     193             : 
     194             :                 u16 material;
     195           0 :                 is>>material;
     196             :                 u16 materialcount;
     197           0 :                 is>>materialcount;
     198           0 :                 if(material > 0xfff)
     199           0 :                         throw SerializationError("Too large material number");
     200             :                 // Convert old id to name
     201           0 :                 NameIdMapping legacy_nimap;
     202           0 :                 content_mapnode_get_name_id_mapping(&legacy_nimap);
     203           0 :                 legacy_nimap.getName(material, name);
     204           0 :                 if(name == "")
     205           0 :                         name = "unknown_block";
     206           0 :                 if (itemdef)
     207           0 :                         name = itemdef->getAlias(name);
     208           0 :                 count = materialcount;
     209             :         }
     210        4596 :         else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
     211        3064 :                         || name == "craft" || name == "CraftItem")
     212             :         {
     213             :                 // Obsoleted on 2012-01-07
     214             : 
     215           0 :                 std::string all;
     216           0 :                 std::getline(is, all, '\n');
     217             :                 // First attempt to read inside ""
     218           0 :                 Strfnd fnd(all);
     219           0 :                 fnd.next("\"");
     220             :                 // If didn't skip to end, we have ""s
     221           0 :                 if(!fnd.atend()){
     222           0 :                         name = fnd.next("\"");
     223             :                 } else { // No luck, just read a word then
     224           0 :                         fnd.start(all);
     225           0 :                         name = fnd.next(" ");
     226             :                 }
     227           0 :                 fnd.skip_over(" ");
     228           0 :                 if (itemdef)
     229           0 :                         name = itemdef->getAlias(name);
     230           0 :                 count = stoi(trim(fnd.next("")));
     231           0 :                 if(count == 0)
     232           0 :                         count = 1;
     233             :         }
     234        1532 :         else if(name == "MBOItem")
     235             :         {
     236             :                 // Obsoleted on 2011-10-14
     237           0 :                 throw SerializationError("MBOItem not supported anymore");
     238             :         }
     239        1532 :         else if(name == "tool" || name == "ToolItem")
     240             :         {
     241             :                 // Obsoleted on 2012-01-07
     242             : 
     243           0 :                 std::string all;
     244           0 :                 std::getline(is, all, '\n');
     245             :                 // First attempt to read inside ""
     246           0 :                 Strfnd fnd(all);
     247           0 :                 fnd.next("\"");
     248             :                 // If didn't skip to end, we have ""s
     249           0 :                 if(!fnd.atend()){
     250           0 :                         name = fnd.next("\"");
     251             :                 } else { // No luck, just read a word then
     252           0 :                         fnd.start(all);
     253           0 :                         name = fnd.next(" ");
     254             :                 }
     255           0 :                 count = 1;
     256             :                 // Then read wear
     257           0 :                 fnd.skip_over(" ");
     258           0 :                 if (itemdef)
     259           0 :                         name = itemdef->getAlias(name);
     260           0 :                 wear = stoi(trim(fnd.next("")));
     261             :         }
     262             :         else
     263             :         {
     264             :                 do  // This loop is just to allow "break;"
     265             :                 {
     266             :                         // The real thing
     267             : 
     268             :                         // Apply item aliases
     269        1532 :                         if (itemdef)
     270        1532 :                                 name = itemdef->getAlias(name);
     271             : 
     272             :                         // Read the count
     273        1627 :                         std::string count_str;
     274        1532 :                         std::getline(is, count_str, ' ');
     275        1532 :                         if(count_str.empty())
     276             :                         {
     277        1197 :                                 count = 1;
     278        1197 :                                 break;
     279             :                         }
     280             :                         else
     281         335 :                                 count = stoi(count_str);
     282             : 
     283             :                         // Read the wear
     284         430 :                         std::string wear_str;
     285         335 :                         std::getline(is, wear_str, ' ');
     286         335 :                         if(wear_str.empty())
     287         240 :                                 break;
     288             :                         else
     289          95 :                                 wear = stoi(wear_str);
     290             : 
     291             :                         // Read metadata
     292          95 :                         metadata = deSerializeJsonStringIfNeeded(is);
     293             : 
     294             :                         // In case fields are added after metadata, skip space here:
     295             :                         //std::getline(is, tmp, ' ');
     296             :                         //if(!tmp.empty())
     297             :                         //      throw SerializationError("Unexpected text after metadata");
     298             : 
     299             :                 } while(false);
     300             :         }
     301             : 
     302        1532 :         if (name.empty() || count == 0)
     303           0 :                 clear();
     304        1532 :         else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
     305         205 :                 count = 1;
     306        1532 : }
     307             : 
     308           0 : void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
     309             : {
     310           0 :         std::istringstream is(str, std::ios::binary);
     311           0 :         deSerialize(is, itemdef);
     312           0 : }
     313             : 
     314           0 : std::string ItemStack::getItemString() const
     315             : {
     316           0 :         std::ostringstream os(std::ios::binary);
     317           0 :         serialize(os);
     318           0 :         return os.str();
     319             : }
     320             : 
     321             : 
     322           0 : ItemStack ItemStack::addItem(const ItemStack &newitem_,
     323             :                 IItemDefManager *itemdef)
     324             : {
     325           0 :         ItemStack newitem = newitem_;
     326             : 
     327             :         // If the item is empty or the position invalid, bail out
     328           0 :         if(newitem.empty())
     329             :         {
     330             :                 // nothing can be added trivially
     331             :         }
     332             :         // If this is an empty item, it's an easy job.
     333           0 :         else if(empty())
     334             :         {
     335           0 :                 *this = newitem;
     336           0 :                 newitem.clear();
     337             :         }
     338             :         // If item name differs, bail out
     339           0 :         else if(name != newitem.name)
     340             :         {
     341             :                 // cannot be added
     342             :         }
     343             :         // If the item fits fully, add counter and delete it
     344           0 :         else if(newitem.count <= freeSpace(itemdef))
     345             :         {
     346           0 :                 add(newitem.count);
     347           0 :                 newitem.clear();
     348             :         }
     349             :         // Else the item does not fit fully. Add all that fits and return
     350             :         // the rest.
     351             :         else
     352             :         {
     353           0 :                 u16 freespace = freeSpace(itemdef);
     354           0 :                 add(freespace);
     355           0 :                 newitem.remove(freespace);
     356             :         }
     357             : 
     358           0 :         return newitem;
     359             : }
     360             : 
     361           0 : bool ItemStack::itemFits(const ItemStack &newitem_,
     362             :                 ItemStack *restitem,
     363             :                 IItemDefManager *itemdef) const
     364             : {
     365           0 :         ItemStack newitem = newitem_;
     366             : 
     367             :         // If the item is empty or the position invalid, bail out
     368           0 :         if(newitem.empty())
     369             :         {
     370             :                 // nothing can be added trivially
     371             :         }
     372             :         // If this is an empty item, it's an easy job.
     373           0 :         else if(empty())
     374             :         {
     375           0 :                 newitem.clear();
     376             :         }
     377             :         // If item name differs, bail out
     378           0 :         else if(name != newitem.name)
     379             :         {
     380             :                 // cannot be added
     381             :         }
     382             :         // If the item fits fully, delete it
     383           0 :         else if(newitem.count <= freeSpace(itemdef))
     384             :         {
     385           0 :                 newitem.clear();
     386             :         }
     387             :         // Else the item does not fit fully. Return the rest.
     388             :         // the rest.
     389             :         else
     390             :         {
     391           0 :                 u16 freespace = freeSpace(itemdef);
     392           0 :                 newitem.remove(freespace);
     393             :         }
     394             : 
     395           0 :         if(restitem)
     396           0 :                 *restitem = newitem;
     397           0 :         return newitem.empty();
     398             : }
     399             : 
     400           0 : ItemStack ItemStack::takeItem(u32 takecount)
     401             : {
     402           0 :         if(takecount == 0 || count == 0)
     403           0 :                 return ItemStack();
     404             : 
     405           0 :         ItemStack result = *this;
     406           0 :         if(takecount >= count)
     407             :         {
     408             :                 // Take all
     409           0 :                 clear();
     410             :         }
     411             :         else
     412             :         {
     413             :                 // Take part
     414           0 :                 remove(takecount);
     415           0 :                 result.count = takecount;
     416             :         }
     417           0 :         return result;
     418             : }
     419             : 
     420           0 : ItemStack ItemStack::peekItem(u32 peekcount) const
     421             : {
     422           0 :         if(peekcount == 0 || count == 0)
     423           0 :                 return ItemStack();
     424             : 
     425           0 :         ItemStack result = *this;
     426           0 :         if(peekcount < count)
     427           0 :                 result.count = peekcount;
     428           0 :         return result;
     429             : }
     430             : 
     431             : /*
     432             :         Inventory
     433             : */
     434             : 
     435         307 : InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef)
     436             : {
     437         307 :         m_name = name;
     438         307 :         m_size = size;
     439         307 :         m_width = 0;
     440         307 :         m_itemdef = itemdef;
     441         307 :         clearItems();
     442             :         //m_dirty = false;
     443         307 : }
     444             : 
     445         604 : InventoryList::~InventoryList()
     446             : {
     447         604 : }
     448             : 
     449         610 : void InventoryList::clearItems()
     450             : {
     451         610 :         m_items.clear();
     452             : 
     453       11101 :         for(u32 i=0; i<m_size; i++)
     454             :         {
     455       10491 :                 m_items.push_back(ItemStack());
     456             :         }
     457             : 
     458             :         //setDirty(true);
     459         610 : }
     460             : 
     461           0 : void InventoryList::setSize(u32 newsize)
     462             : {
     463           0 :         if(newsize != m_items.size())
     464           0 :                 m_items.resize(newsize);
     465           0 :         m_size = newsize;
     466           0 : }
     467             : 
     468           1 : void InventoryList::setWidth(u32 newwidth)
     469             : {
     470           1 :         m_width = newwidth;
     471           1 : }
     472             : 
     473           0 : void InventoryList::setName(const std::string &name)
     474             : {
     475           0 :         m_name = name;
     476           0 : }
     477             : 
     478          44 : void InventoryList::serialize(std::ostream &os) const
     479             : {
     480             :         //os.imbue(std::locale("C"));
     481             :         
     482          44 :         os<<"Width "<<m_width<<"\n";
     483             : 
     484        1265 :         for(u32 i=0; i<m_items.size(); i++)
     485             :         {
     486        1221 :                 const ItemStack &item = m_items[i];
     487        1221 :                 if(item.empty())
     488             :                 {
     489        1166 :                         os<<"Empty";
     490             :                 }
     491             :                 else
     492             :                 {
     493          55 :                         os<<"Item ";
     494          55 :                         item.serialize(os);
     495             :                 }
     496        1221 :                 os<<"\n";
     497             :         }
     498             : 
     499          44 :         os<<"EndInventoryList\n";
     500          44 : }
     501             : 
     502         303 : void InventoryList::deSerialize(std::istream &is)
     503             : {
     504             :         //is.imbue(std::locale("C"));
     505             : 
     506         303 :         clearItems();
     507         303 :         u32 item_i = 0;
     508         303 :         m_width = 0;
     509             : 
     510        5527 :         for(;;)
     511             :         {
     512       11357 :                 std::string line;
     513        5830 :                 std::getline(is, line, '\n');
     514             : 
     515       11357 :                 std::istringstream iss(line);
     516             :                 //iss.imbue(std::locale("C"));
     517             : 
     518       11357 :                 std::string name;
     519        5830 :                 std::getline(iss, name, ' ');
     520             : 
     521        5830 :                 if(name == "EndInventoryList")
     522             :                 {
     523         303 :                         break;
     524             :                 }
     525             :                 // This is a temporary backwards compatibility fix
     526        5527 :                 else if(name == "end")
     527             :                 {
     528           0 :                         break;
     529             :                 }
     530        5527 :                 else if(name == "Width")
     531             :                 {
     532         303 :                         iss >> m_width;
     533         303 :                         if (iss.fail())
     534           0 :                                 throw SerializationError("incorrect width property");
     535             :                 }
     536        5224 :                 else if(name == "Item")
     537             :                 {
     538        1532 :                         if(item_i > getSize() - 1)
     539           0 :                                 throw SerializationError("too many items");
     540        3064 :                         ItemStack item;
     541        1532 :                         item.deSerialize(iss, m_itemdef);
     542        1532 :                         m_items[item_i++] = item;
     543             :                 }
     544        3692 :                 else if(name == "Empty")
     545             :                 {
     546        3692 :                         if(item_i > getSize() - 1)
     547           0 :                                 throw SerializationError("too many items");
     548        3692 :                         m_items[item_i++].clear();
     549             :                 }
     550             :         }
     551         303 : }
     552             : 
     553         297 : InventoryList::InventoryList(const InventoryList &other)
     554             : {
     555         297 :         *this = other;
     556         297 : }
     557             : 
     558         297 : InventoryList & InventoryList::operator = (const InventoryList &other)
     559             : {
     560         297 :         m_items = other.m_items;
     561         297 :         m_size = other.m_size;
     562         297 :         m_width = other.m_width;
     563         297 :         m_name = other.m_name;
     564         297 :         m_itemdef = other.m_itemdef;
     565             :         //setDirty(true);
     566             : 
     567         297 :         return *this;
     568             : }
     569             : 
     570           0 : bool InventoryList::operator == (const InventoryList &other) const
     571             : {
     572           0 :         if(m_size != other.m_size)
     573           0 :                 return false;
     574           0 :         if(m_width != other.m_width)
     575           0 :                 return false;
     576           0 :         if(m_name != other.m_name)
     577           0 :                 return false;
     578           0 :         for(u32 i=0; i<m_items.size(); i++)
     579             :         {
     580           0 :                 ItemStack s1 = m_items[i];
     581           0 :                 ItemStack s2 = other.m_items[i];
     582           0 :                 if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
     583           0 :                                 s1.metadata != s2.metadata)
     584           0 :                         return false;
     585             :         }
     586             : 
     587           0 :         return true;
     588             : }
     589             : 
     590        3570 : const std::string &InventoryList::getName() const
     591             : {
     592        3570 :         return m_name;
     593             : }
     594             : 
     595       16950 : u32 InventoryList::getSize() const
     596             : {
     597       16950 :         return m_items.size();
     598             : }
     599             : 
     600           0 : u32 InventoryList::getWidth() const
     601             : {
     602           0 :         return m_width;
     603             : }
     604             : 
     605           0 : u32 InventoryList::getUsedSlots() const
     606             : {
     607           0 :         u32 num = 0;
     608           0 :         for(u32 i=0; i<m_items.size(); i++)
     609             :         {
     610           0 :                 if(!m_items[i].empty())
     611           0 :                         num++;
     612             :         }
     613           0 :         return num;
     614             : }
     615             : 
     616           0 : u32 InventoryList::getFreeSlots() const
     617             : {
     618           0 :         return getSize() - getUsedSlots();
     619             : }
     620             : 
     621           0 : const ItemStack& InventoryList::getItem(u32 i) const
     622             : {
     623             :         assert(i < m_size); // Pre-condition
     624           0 :         return m_items[i];
     625             : }
     626             : 
     627       11682 : ItemStack& InventoryList::getItem(u32 i)
     628             : {
     629             :         assert(i < m_size); // Pre-condition
     630       11682 :         return m_items[i];
     631             : }
     632             : 
     633           0 : ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
     634             : {
     635           0 :         if(i >= m_items.size())
     636           0 :                 return newitem;
     637             : 
     638           0 :         ItemStack olditem = m_items[i];
     639           0 :         m_items[i] = newitem;
     640             :         //setDirty(true);
     641           0 :         return olditem;
     642             : }
     643             : 
     644           0 : void InventoryList::deleteItem(u32 i)
     645             : {
     646             :         assert(i < m_items.size()); // Pre-condition
     647           0 :         m_items[i].clear();
     648           0 : }
     649             : 
     650           0 : ItemStack InventoryList::addItem(const ItemStack &newitem_)
     651             : {
     652           0 :         ItemStack newitem = newitem_;
     653             : 
     654           0 :         if(newitem.empty())
     655           0 :                 return newitem;
     656             :         
     657             :         /*
     658             :                 First try to find if it could be added to some existing items
     659             :         */
     660           0 :         for(u32 i=0; i<m_items.size(); i++)
     661             :         {
     662             :                 // Ignore empty slots
     663           0 :                 if(m_items[i].empty())
     664           0 :                         continue;
     665             :                 // Try adding
     666           0 :                 newitem = addItem(i, newitem);
     667           0 :                 if(newitem.empty())
     668           0 :                         return newitem; // All was eaten
     669             :         }
     670             : 
     671             :         /*
     672             :                 Then try to add it to empty slots
     673             :         */
     674           0 :         for(u32 i=0; i<m_items.size(); i++)
     675             :         {
     676             :                 // Ignore unempty slots
     677           0 :                 if(!m_items[i].empty())
     678           0 :                         continue;
     679             :                 // Try adding
     680           0 :                 newitem = addItem(i, newitem);
     681           0 :                 if(newitem.empty())
     682           0 :                         return newitem; // All was eaten
     683             :         }
     684             : 
     685             :         // Return leftover
     686           0 :         return newitem;
     687             : }
     688             : 
     689           0 : ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
     690             : {
     691           0 :         if(i >= m_items.size())
     692           0 :                 return newitem;
     693             : 
     694           0 :         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
     695             :         //if(leftover != newitem)
     696             :         //      setDirty(true);
     697           0 :         return leftover;
     698             : }
     699             : 
     700           0 : bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
     701             :                 ItemStack *restitem) const
     702             : {
     703           0 :         if(i >= m_items.size())
     704             :         {
     705           0 :                 if(restitem)
     706           0 :                         *restitem = newitem;
     707           0 :                 return false;
     708             :         }
     709             : 
     710           0 :         return m_items[i].itemFits(newitem, restitem, m_itemdef);
     711             : }
     712             : 
     713           0 : bool InventoryList::roomForItem(const ItemStack &item_) const
     714             : {
     715           0 :         ItemStack item = item_;
     716           0 :         ItemStack leftover;
     717           0 :         for(u32 i=0; i<m_items.size(); i++)
     718             :         {
     719           0 :                 if(itemFits(i, item, &leftover))
     720           0 :                         return true;
     721           0 :                 item = leftover;
     722             :         }
     723           0 :         return false;
     724             : }
     725             : 
     726           0 : bool InventoryList::containsItem(const ItemStack &item) const
     727             : {
     728           0 :         u32 count = item.count;
     729           0 :         if(count == 0)
     730           0 :                 return true;
     731           0 :         for(std::vector<ItemStack>::const_reverse_iterator
     732           0 :                         i = m_items.rbegin();
     733           0 :                         i != m_items.rend(); i++)
     734             :         {
     735           0 :                 if(count == 0)
     736           0 :                         break;
     737           0 :                 if(i->name == item.name)
     738             :                 {
     739           0 :                         if(i->count >= count)
     740           0 :                                 return true;
     741             :                         else
     742           0 :                                 count -= i->count;
     743             :                 }
     744             :         }
     745           0 :         return false;
     746             : }
     747             : 
     748           0 : ItemStack InventoryList::removeItem(const ItemStack &item)
     749             : {
     750           0 :         ItemStack removed;
     751           0 :         for(std::vector<ItemStack>::reverse_iterator
     752           0 :                         i = m_items.rbegin();
     753           0 :                         i != m_items.rend(); i++)
     754             :         {
     755           0 :                 if(i->name == item.name)
     756             :                 {
     757           0 :                         u32 still_to_remove = item.count - removed.count;
     758           0 :                         removed.addItem(i->takeItem(still_to_remove), m_itemdef);
     759           0 :                         if(removed.count == item.count)
     760           0 :                                 break;
     761             :                 }
     762             :         }
     763           0 :         return removed;
     764             : }
     765             : 
     766           0 : ItemStack InventoryList::takeItem(u32 i, u32 takecount)
     767             : {
     768           0 :         if(i >= m_items.size())
     769           0 :                 return ItemStack();
     770             : 
     771           0 :         ItemStack taken = m_items[i].takeItem(takecount);
     772             :         //if(!taken.empty())
     773             :         //      setDirty(true);
     774           0 :         return taken;
     775             : }
     776             : 
     777           0 : ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
     778             : {
     779           0 :         if(i >= m_items.size())
     780           0 :                 return ItemStack();
     781             : 
     782           0 :         return m_items[i].peekItem(peekcount);
     783             : }
     784             : 
     785           0 : void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
     786             : {
     787             :         // Take item from source list
     788           0 :         ItemStack item1;
     789           0 :         if (count == 0)
     790           0 :                 item1 = changeItem(i, ItemStack());
     791             :         else
     792           0 :                 item1 = takeItem(i, count);
     793             : 
     794           0 :         if (item1.empty())
     795           0 :                 return;
     796             : 
     797             :         // Try to add the item to destination list
     798           0 :         u32 dest_size = dest->getSize();
     799             :         // First try all the non-empty slots
     800           0 :         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
     801           0 :                 if (!m_items[dest_i].empty()) {
     802           0 :                         item1 = dest->addItem(dest_i, item1);
     803           0 :                         if (item1.empty()) return;
     804             :                 }
     805             :         }
     806             : 
     807             :         // Then try all the empty ones
     808           0 :         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
     809           0 :                 if (m_items[dest_i].empty()) {
     810           0 :                         item1 = dest->addItem(dest_i, item1);
     811           0 :                         if (item1.empty()) return;
     812             :                 }
     813             :         }
     814             : 
     815             :         // If we reach this, the item was not fully added
     816             :         // Add the remaining part back to the source item
     817           0 :         addItem(i, item1);
     818             : }
     819             : 
     820           0 : u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
     821             :                 u32 count, bool swap_if_needed)
     822             : {
     823           0 :         if(this == dest && i == dest_i)
     824           0 :                 return count;
     825             : 
     826             :         // Take item from source list
     827           0 :         ItemStack item1;
     828           0 :         if(count == 0)
     829           0 :                 item1 = changeItem(i, ItemStack());
     830             :         else
     831           0 :                 item1 = takeItem(i, count);
     832             : 
     833           0 :         if(item1.empty())
     834           0 :                 return 0;
     835             : 
     836             :         // Try to add the item to destination list
     837           0 :         u32 oldcount = item1.count;
     838           0 :         item1 = dest->addItem(dest_i, item1);
     839             : 
     840             :         // If something is returned, the item was not fully added
     841           0 :         if(!item1.empty())
     842             :         {
     843             :                 // If olditem is returned, nothing was added.
     844           0 :                 bool nothing_added = (item1.count == oldcount);
     845             : 
     846             :                 // If something else is returned, part of the item was left unadded.
     847             :                 // Add the other part back to the source item
     848           0 :                 addItem(i, item1);
     849             : 
     850             :                 // If olditem is returned, nothing was added.
     851             :                 // Swap the items
     852           0 :                 if (nothing_added && swap_if_needed) {
     853             :                         // Take item from source list
     854           0 :                         item1 = changeItem(i, ItemStack());
     855             :                         // Adding was not possible, swap the items.
     856           0 :                         ItemStack item2 = dest->changeItem(dest_i, item1);
     857             :                         // Put item from destination list to the source list
     858           0 :                         changeItem(i, item2);
     859             :                 }
     860             :         }
     861           0 :         return (oldcount - item1.count);
     862             : }
     863             : 
     864             : /*
     865             :         Inventory
     866             : */
     867             : 
     868      373646 : Inventory::~Inventory()
     869             : {
     870      186823 :         clear();
     871      186823 : }
     872             : 
     873      373697 : void Inventory::clear()
     874             : {
     875      373697 :         m_dirty = true;
     876      374301 :         for(u32 i=0; i<m_lists.size(); i++)
     877             :         {
     878         604 :                 delete m_lists[i];
     879             :         }
     880      373697 :         m_lists.clear();
     881      373697 : }
     882             : 
     883           0 : void Inventory::clearContents()
     884             : {
     885           0 :         m_dirty = true;
     886           0 :         for(u32 i=0; i<m_lists.size(); i++)
     887             :         {
     888           0 :                 InventoryList *list = m_lists[i];
     889           0 :                 for(u32 j=0; j<list->getSize(); j++)
     890             :                 {
     891           0 :                         list->deleteItem(j);
     892             :                 }
     893             :         }
     894           0 : }
     895             : 
     896      186817 : Inventory::Inventory(IItemDefManager *itemdef)
     897             : {
     898      186817 :         m_dirty = false;
     899      186817 :         m_itemdef = itemdef;
     900      186817 : }
     901             : 
     902           6 : Inventory::Inventory(const Inventory &other)
     903             : {
     904           6 :         *this = other;
     905           6 :         m_dirty = false;
     906           6 : }
     907             : 
     908          33 : Inventory & Inventory::operator = (const Inventory &other)
     909             : {
     910             :         // Gracefully handle self assignment
     911          33 :         if(this != &other)
     912             :         {
     913          33 :                 m_dirty = true;
     914          33 :                 clear();
     915          33 :                 m_itemdef = other.m_itemdef;
     916         330 :                 for(u32 i=0; i<other.m_lists.size(); i++)
     917             :                 {
     918         297 :                         m_lists.push_back(new InventoryList(*other.m_lists[i]));
     919             :                 }
     920             :         }
     921          33 :         return *this;
     922             : }
     923             : 
     924           0 : bool Inventory::operator == (const Inventory &other) const
     925             : {
     926           0 :         if(m_lists.size() != other.m_lists.size())
     927           0 :                 return false;
     928             : 
     929           0 :         for(u32 i=0; i<m_lists.size(); i++)
     930             :         {
     931           0 :                 if(*m_lists[i] != *other.m_lists[i])
     932           0 :                         return false;
     933             :         }
     934           0 :         return true;
     935             : }
     936             : 
     937          23 : void Inventory::serialize(std::ostream &os) const
     938             : {
     939          67 :         for(u32 i=0; i<m_lists.size(); i++)
     940             :         {
     941          44 :                 InventoryList *list = m_lists[i];
     942          44 :                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
     943          44 :                 list->serialize(os);
     944             :         }
     945             : 
     946          23 :         os<<"EndInventory\n";
     947          23 : }
     948             : 
     949      186840 : void Inventory::deSerialize(std::istream &is)
     950             : {
     951      186840 :         clear();
     952             : 
     953         303 :         for(;;)
     954             :         {
     955      187446 :                 std::string line;
     956      187143 :                 std::getline(is, line, '\n');
     957             : 
     958      187446 :                 std::istringstream iss(line);
     959             : 
     960      187446 :                 std::string name;
     961      187143 :                 std::getline(iss, name, ' ');
     962             : 
     963      187143 :                 if(name == "EndInventory")
     964             :                 {
     965      186840 :                         break;
     966             :                 }
     967             :                 // This is a temporary backwards compatibility fix
     968         303 :                 else if(name == "end")
     969             :                 {
     970           0 :                         break;
     971             :                 }
     972         303 :                 else if(name == "List")
     973             :                 {
     974         606 :                         std::string listname;
     975             :                         u32 listsize;
     976             : 
     977         303 :                         std::getline(iss, listname, ' ');
     978         303 :                         iss>>listsize;
     979             : 
     980         303 :                         InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
     981         303 :                         list->deSerialize(is);
     982             : 
     983         303 :                         m_lists.push_back(list);
     984             :                 }
     985             :                 else
     986             :                 {
     987           0 :                         throw SerializationError("invalid inventory specifier: " + name);
     988             :                 }
     989             :         }
     990      186840 : }
     991             : 
     992           4 : InventoryList * Inventory::addList(const std::string &name, u32 size)
     993             : {
     994           4 :         m_dirty = true;
     995           4 :         s32 i = getListIndex(name);
     996           4 :         if(i != -1)
     997             :         {
     998           0 :                 if(m_lists[i]->getSize() != size)
     999             :                 {
    1000           0 :                         delete m_lists[i];
    1001           0 :                         m_lists[i] = new InventoryList(name, size, m_itemdef);
    1002             :                 }
    1003           0 :                 return m_lists[i];
    1004             :         }
    1005             :         else
    1006             :         {
    1007             :                 //don't create list with invalid name
    1008           4 :                 if (name.find(" ") != std::string::npos) return NULL;
    1009             : 
    1010           4 :                 InventoryList *list = new InventoryList(name, size, m_itemdef);
    1011           4 :                 m_lists.push_back(list);
    1012           4 :                 return list;
    1013             :         }
    1014             : }
    1015             : 
    1016        3522 : InventoryList * Inventory::getList(const std::string &name)
    1017             : {
    1018        3522 :         s32 i = getListIndex(name);
    1019        3522 :         if(i == -1)
    1020           2 :                 return NULL;
    1021        3520 :         return m_lists[i];
    1022             : }
    1023             : 
    1024           0 : std::vector<const InventoryList*> Inventory::getLists()
    1025             : {
    1026           0 :         std::vector<const InventoryList*> lists;
    1027           0 :         for(u32 i=0; i<m_lists.size(); i++)
    1028             :         {
    1029           0 :                 InventoryList *list = m_lists[i];
    1030           0 :                 lists.push_back(list);
    1031             :         }
    1032           0 :         return lists;
    1033             : }
    1034             : 
    1035           0 : bool Inventory::deleteList(const std::string &name)
    1036             : {
    1037           0 :         s32 i = getListIndex(name);
    1038           0 :         if(i == -1)
    1039           0 :                 return false;
    1040           0 :         m_dirty = true;
    1041           0 :         delete m_lists[i];
    1042           0 :         m_lists.erase(m_lists.begin() + i);
    1043           0 :         return true;
    1044             : }
    1045             : 
    1046           0 : const InventoryList * Inventory::getList(const std::string &name) const
    1047             : {
    1048           0 :         s32 i = getListIndex(name);
    1049           0 :         if(i == -1)
    1050           0 :                 return NULL;
    1051           0 :         return m_lists[i];
    1052             : }
    1053             : 
    1054        3526 : const s32 Inventory::getListIndex(const std::string &name) const
    1055             : {
    1056        3532 :         for(u32 i=0; i<m_lists.size(); i++)
    1057             :         {
    1058        3526 :                 if(m_lists[i]->getName() == name)
    1059        3520 :                         return i;
    1060             :         }
    1061           6 :         return -1;
    1062           3 : }
    1063             : 
    1064             : //END

Generated by: LCOV version 1.11