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