]> git.lizzy.rs Git - dragonfireclient.git/blob - src/inventory.cpp
Document zoom_fov in settingtypes.txt and minetest.conf.example
[dragonfireclient.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 "util/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.at_end()){
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.at_end()){
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 or metadata differs, bail out 
339         else if (name != newitem.name
340                 || metadata != newitem.metadata)
341         {
342                 // cannot be added
343         }
344         // If the item fits fully, add counter and delete it
345         else if(newitem.count <= freeSpace(itemdef))
346         {
347                 add(newitem.count);
348                 newitem.clear();
349         }
350         // Else the item does not fit fully. Add all that fits and return
351         // the rest.
352         else
353         {
354                 u16 freespace = freeSpace(itemdef);
355                 add(freespace);
356                 newitem.remove(freespace);
357         }
358
359         return newitem;
360 }
361
362 bool ItemStack::itemFits(const ItemStack &newitem_,
363                 ItemStack *restitem,
364                 IItemDefManager *itemdef) const
365 {
366         ItemStack newitem = newitem_;
367
368         // If the item is empty or the position invalid, bail out
369         if(newitem.empty())
370         {
371                 // nothing can be added trivially
372         }
373         // If this is an empty item, it's an easy job.
374         else if(empty())
375         {
376                 newitem.clear();
377         }
378         // If item name or metadata differs, bail out 
379         else if (name != newitem.name
380                 || metadata != newitem.metadata)
381         {
382                 // cannot be added
383         }
384         // If the item fits fully, delete it
385         else if(newitem.count <= freeSpace(itemdef))
386         {
387                 newitem.clear();
388         }
389         // Else the item does not fit fully. Return the rest.
390         // the rest.
391         else
392         {
393                 u16 freespace = freeSpace(itemdef);
394                 newitem.remove(freespace);
395         }
396
397         if(restitem)
398                 *restitem = newitem;
399         return newitem.empty();
400 }
401
402 ItemStack ItemStack::takeItem(u32 takecount)
403 {
404         if(takecount == 0 || count == 0)
405                 return ItemStack();
406
407         ItemStack result = *this;
408         if(takecount >= count)
409         {
410                 // Take all
411                 clear();
412         }
413         else
414         {
415                 // Take part
416                 remove(takecount);
417                 result.count = takecount;
418         }
419         return result;
420 }
421
422 ItemStack ItemStack::peekItem(u32 peekcount) const
423 {
424         if(peekcount == 0 || count == 0)
425                 return ItemStack();
426
427         ItemStack result = *this;
428         if(peekcount < count)
429                 result.count = peekcount;
430         return result;
431 }
432
433 /*
434         Inventory
435 */
436
437 InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef)
438 {
439         m_name = name;
440         m_size = size;
441         m_width = 0;
442         m_itemdef = itemdef;
443         clearItems();
444         //m_dirty = false;
445 }
446
447 InventoryList::~InventoryList()
448 {
449 }
450
451 void InventoryList::clearItems()
452 {
453         m_items.clear();
454
455         for(u32 i=0; i<m_size; i++)
456         {
457                 m_items.push_back(ItemStack());
458         }
459
460         //setDirty(true);
461 }
462
463 void InventoryList::setSize(u32 newsize)
464 {
465         if(newsize != m_items.size())
466                 m_items.resize(newsize);
467         m_size = newsize;
468 }
469
470 void InventoryList::setWidth(u32 newwidth)
471 {
472         m_width = newwidth;
473 }
474
475 void InventoryList::setName(const std::string &name)
476 {
477         m_name = name;
478 }
479
480 void InventoryList::serialize(std::ostream &os) const
481 {
482         //os.imbue(std::locale("C"));
483
484         os<<"Width "<<m_width<<"\n";
485
486         for(u32 i=0; i<m_items.size(); i++)
487         {
488                 const ItemStack &item = m_items[i];
489                 if(item.empty())
490                 {
491                         os<<"Empty";
492                 }
493                 else
494                 {
495                         os<<"Item ";
496                         item.serialize(os);
497                 }
498                 os<<"\n";
499         }
500
501         os<<"EndInventoryList\n";
502 }
503
504 void InventoryList::deSerialize(std::istream &is)
505 {
506         //is.imbue(std::locale("C"));
507
508         clearItems();
509         u32 item_i = 0;
510         m_width = 0;
511
512         for(;;)
513         {
514                 std::string line;
515                 std::getline(is, line, '\n');
516
517                 std::istringstream iss(line);
518                 //iss.imbue(std::locale("C"));
519
520                 std::string name;
521                 std::getline(iss, name, ' ');
522
523                 if(name == "EndInventoryList")
524                 {
525                         break;
526                 }
527                 // This is a temporary backwards compatibility fix
528                 else if(name == "end")
529                 {
530                         break;
531                 }
532                 else if(name == "Width")
533                 {
534                         iss >> m_width;
535                         if (iss.fail())
536                                 throw SerializationError("incorrect width property");
537                 }
538                 else if(name == "Item")
539                 {
540                         if(item_i > getSize() - 1)
541                                 throw SerializationError("too many items");
542                         ItemStack item;
543                         item.deSerialize(iss, m_itemdef);
544                         m_items[item_i++] = item;
545                 }
546                 else if(name == "Empty")
547                 {
548                         if(item_i > getSize() - 1)
549                                 throw SerializationError("too many items");
550                         m_items[item_i++].clear();
551                 }
552         }
553 }
554
555 InventoryList::InventoryList(const InventoryList &other)
556 {
557         *this = other;
558 }
559
560 InventoryList & InventoryList::operator = (const InventoryList &other)
561 {
562         m_items = other.m_items;
563         m_size = other.m_size;
564         m_width = other.m_width;
565         m_name = other.m_name;
566         m_itemdef = other.m_itemdef;
567         //setDirty(true);
568
569         return *this;
570 }
571
572 bool InventoryList::operator == (const InventoryList &other) const
573 {
574         if(m_size != other.m_size)
575                 return false;
576         if(m_width != other.m_width)
577                 return false;
578         if(m_name != other.m_name)
579                 return false;
580         for(u32 i=0; i<m_items.size(); i++)
581         {
582                 ItemStack s1 = m_items[i];
583                 ItemStack s2 = other.m_items[i];
584                 if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
585                                 s1.metadata != s2.metadata)
586                         return false;
587         }
588
589         return true;
590 }
591
592 const std::string &InventoryList::getName() const
593 {
594         return m_name;
595 }
596
597 u32 InventoryList::getSize() const
598 {
599         return m_items.size();
600 }
601
602 u32 InventoryList::getWidth() const
603 {
604         return m_width;
605 }
606
607 u32 InventoryList::getUsedSlots() const
608 {
609         u32 num = 0;
610         for(u32 i=0; i<m_items.size(); i++)
611         {
612                 if(!m_items[i].empty())
613                         num++;
614         }
615         return num;
616 }
617
618 u32 InventoryList::getFreeSlots() const
619 {
620         return getSize() - getUsedSlots();
621 }
622
623 const ItemStack& InventoryList::getItem(u32 i) const
624 {
625         assert(i < m_size); // Pre-condition
626         return m_items[i];
627 }
628
629 ItemStack& InventoryList::getItem(u32 i)
630 {
631         assert(i < m_size); // Pre-condition
632         return m_items[i];
633 }
634
635 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
636 {
637         if(i >= m_items.size())
638                 return newitem;
639
640         ItemStack olditem = m_items[i];
641         m_items[i] = newitem;
642         //setDirty(true);
643         return olditem;
644 }
645
646 void InventoryList::deleteItem(u32 i)
647 {
648         assert(i < m_items.size()); // Pre-condition
649         m_items[i].clear();
650 }
651
652 ItemStack InventoryList::addItem(const ItemStack &newitem_)
653 {
654         ItemStack newitem = newitem_;
655
656         if(newitem.empty())
657                 return newitem;
658
659         /*
660                 First try to find if it could be added to some existing items
661         */
662         for(u32 i=0; i<m_items.size(); i++)
663         {
664                 // Ignore empty slots
665                 if(m_items[i].empty())
666                         continue;
667                 // Try adding
668                 newitem = addItem(i, newitem);
669                 if(newitem.empty())
670                         return newitem; // All was eaten
671         }
672
673         /*
674                 Then try to add it to empty slots
675         */
676         for(u32 i=0; i<m_items.size(); i++)
677         {
678                 // Ignore unempty slots
679                 if(!m_items[i].empty())
680                         continue;
681                 // Try adding
682                 newitem = addItem(i, newitem);
683                 if(newitem.empty())
684                         return newitem; // All was eaten
685         }
686
687         // Return leftover
688         return newitem;
689 }
690
691 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
692 {
693         if(i >= m_items.size())
694                 return newitem;
695
696         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
697         //if(leftover != newitem)
698         //      setDirty(true);
699         return leftover;
700 }
701
702 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
703                 ItemStack *restitem) const
704 {
705         if(i >= m_items.size())
706         {
707                 if(restitem)
708                         *restitem = newitem;
709                 return false;
710         }
711
712         return m_items[i].itemFits(newitem, restitem, m_itemdef);
713 }
714
715 bool InventoryList::roomForItem(const ItemStack &item_) const
716 {
717         ItemStack item = item_;
718         ItemStack leftover;
719         for(u32 i=0; i<m_items.size(); i++)
720         {
721                 if(itemFits(i, item, &leftover))
722                         return true;
723                 item = leftover;
724         }
725         return false;
726 }
727
728 bool InventoryList::containsItem(const ItemStack &item) const
729 {
730         u32 count = item.count;
731         if(count == 0)
732                 return true;
733         for(std::vector<ItemStack>::const_reverse_iterator
734                         i = m_items.rbegin();
735                         i != m_items.rend(); ++i)
736         {
737                 if(count == 0)
738                         break;
739                 if(i->name == item.name)
740                 {
741                         if(i->count >= count)
742                                 return true;
743                         else
744                                 count -= i->count;
745                 }
746         }
747         return false;
748 }
749
750 ItemStack InventoryList::removeItem(const ItemStack &item)
751 {
752         ItemStack removed;
753         for(std::vector<ItemStack>::reverse_iterator
754                         i = m_items.rbegin();
755                         i != m_items.rend(); ++i)
756         {
757                 if(i->name == item.name)
758                 {
759                         u32 still_to_remove = item.count - removed.count;
760                         removed.addItem(i->takeItem(still_to_remove), m_itemdef);
761                         if(removed.count == item.count)
762                                 break;
763                 }
764         }
765         return removed;
766 }
767
768 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
769 {
770         if(i >= m_items.size())
771                 return ItemStack();
772
773         ItemStack taken = m_items[i].takeItem(takecount);
774         //if(!taken.empty())
775         //      setDirty(true);
776         return taken;
777 }
778
779 ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
780 {
781         if(i >= m_items.size())
782                 return ItemStack();
783
784         return m_items[i].peekItem(peekcount);
785 }
786
787 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
788 {
789         // Take item from source list
790         ItemStack item1;
791         if (count == 0)
792                 item1 = changeItem(i, ItemStack());
793         else
794                 item1 = takeItem(i, count);
795
796         if (item1.empty())
797                 return;
798
799         // Try to add the item to destination list
800         u32 dest_size = dest->getSize();
801         // First try all the non-empty slots
802         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
803                 if (!m_items[dest_i].empty()) {
804                         item1 = dest->addItem(dest_i, item1);
805                         if (item1.empty()) return;
806                 }
807         }
808
809         // Then try all the empty ones
810         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
811                 if (m_items[dest_i].empty()) {
812                         item1 = dest->addItem(dest_i, item1);
813                         if (item1.empty()) return;
814                 }
815         }
816
817         // If we reach this, the item was not fully added
818         // Add the remaining part back to the source item
819         addItem(i, item1);
820 }
821
822 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
823                 u32 count, bool swap_if_needed, bool *did_swap)
824 {
825         if(this == dest && i == dest_i)
826                 return count;
827
828         // Take item from source list
829         ItemStack item1;
830         if(count == 0)
831                 item1 = changeItem(i, ItemStack());
832         else
833                 item1 = takeItem(i, count);
834
835         if(item1.empty())
836                 return 0;
837
838         // Try to add the item to destination list
839         u32 oldcount = item1.count;
840         item1 = dest->addItem(dest_i, item1);
841
842         // If something is returned, the item was not fully added
843         if(!item1.empty())
844         {
845                 // If olditem is returned, nothing was added.
846                 bool nothing_added = (item1.count == oldcount);
847
848                 // If something else is returned, part of the item was left unadded.
849                 // Add the other part back to the source item
850                 addItem(i, item1);
851
852                 // If olditem is returned, nothing was added.
853                 // Swap the items
854                 if (nothing_added && swap_if_needed) {
855                         // Tell that we swapped
856                         if (did_swap != NULL) {
857                                 *did_swap = true;
858                         }
859                         // Take item from source list
860                         item1 = changeItem(i, ItemStack());
861                         // Adding was not possible, swap the items.
862                         ItemStack item2 = dest->changeItem(dest_i, item1);
863                         // Put item from destination list to the source list
864                         changeItem(i, item2);
865                 }
866         }
867         return (oldcount - item1.count);
868 }
869
870 /*
871         Inventory
872 */
873
874 Inventory::~Inventory()
875 {
876         clear();
877 }
878
879 void Inventory::clear()
880 {
881         m_dirty = true;
882         for(u32 i=0; i<m_lists.size(); i++)
883         {
884                 delete m_lists[i];
885         }
886         m_lists.clear();
887 }
888
889 void Inventory::clearContents()
890 {
891         m_dirty = true;
892         for(u32 i=0; i<m_lists.size(); i++)
893         {
894                 InventoryList *list = m_lists[i];
895                 for(u32 j=0; j<list->getSize(); j++)
896                 {
897                         list->deleteItem(j);
898                 }
899         }
900 }
901
902 Inventory::Inventory(IItemDefManager *itemdef)
903 {
904         m_dirty = false;
905         m_itemdef = itemdef;
906 }
907
908 Inventory::Inventory(const Inventory &other)
909 {
910         *this = other;
911         m_dirty = false;
912 }
913
914 Inventory & Inventory::operator = (const Inventory &other)
915 {
916         // Gracefully handle self assignment
917         if(this != &other)
918         {
919                 m_dirty = true;
920                 clear();
921                 m_itemdef = other.m_itemdef;
922                 for(u32 i=0; i<other.m_lists.size(); i++)
923                 {
924                         m_lists.push_back(new InventoryList(*other.m_lists[i]));
925                 }
926         }
927         return *this;
928 }
929
930 bool Inventory::operator == (const Inventory &other) const
931 {
932         if(m_lists.size() != other.m_lists.size())
933                 return false;
934
935         for(u32 i=0; i<m_lists.size(); i++)
936         {
937                 if(*m_lists[i] != *other.m_lists[i])
938                         return false;
939         }
940         return true;
941 }
942
943 void Inventory::serialize(std::ostream &os) const
944 {
945         for(u32 i=0; i<m_lists.size(); i++)
946         {
947                 InventoryList *list = m_lists[i];
948                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
949                 list->serialize(os);
950         }
951
952         os<<"EndInventory\n";
953 }
954
955 void Inventory::deSerialize(std::istream &is)
956 {
957         clear();
958
959         for(;;)
960         {
961                 std::string line;
962                 std::getline(is, line, '\n');
963
964                 std::istringstream iss(line);
965
966                 std::string name;
967                 std::getline(iss, name, ' ');
968
969                 if(name == "EndInventory")
970                 {
971                         break;
972                 }
973                 // This is a temporary backwards compatibility fix
974                 else if(name == "end")
975                 {
976                         break;
977                 }
978                 else if(name == "List")
979                 {
980                         std::string listname;
981                         u32 listsize;
982
983                         std::getline(iss, listname, ' ');
984                         iss>>listsize;
985
986                         InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
987                         list->deSerialize(is);
988
989                         m_lists.push_back(list);
990                 }
991                 else
992                 {
993                         throw SerializationError("invalid inventory specifier: " + name);
994                 }
995         }
996 }
997
998 InventoryList * Inventory::addList(const std::string &name, u32 size)
999 {
1000         m_dirty = true;
1001         s32 i = getListIndex(name);
1002         if(i != -1)
1003         {
1004                 if(m_lists[i]->getSize() != size)
1005                 {
1006                         delete m_lists[i];
1007                         m_lists[i] = new InventoryList(name, size, m_itemdef);
1008                 }
1009                 return m_lists[i];
1010         }
1011         else
1012         {
1013                 //don't create list with invalid name
1014                 if (name.find(" ") != std::string::npos) return NULL;
1015
1016                 InventoryList *list = new InventoryList(name, size, m_itemdef);
1017                 m_lists.push_back(list);
1018                 return list;
1019         }
1020 }
1021
1022 InventoryList * Inventory::getList(const std::string &name)
1023 {
1024         s32 i = getListIndex(name);
1025         if(i == -1)
1026                 return NULL;
1027         return m_lists[i];
1028 }
1029
1030 std::vector<const InventoryList*> Inventory::getLists()
1031 {
1032         std::vector<const InventoryList*> lists;
1033         for(u32 i=0; i<m_lists.size(); i++)
1034         {
1035                 InventoryList *list = m_lists[i];
1036                 lists.push_back(list);
1037         }
1038         return lists;
1039 }
1040
1041 bool Inventory::deleteList(const std::string &name)
1042 {
1043         s32 i = getListIndex(name);
1044         if(i == -1)
1045                 return false;
1046         m_dirty = true;
1047         delete m_lists[i];
1048         m_lists.erase(m_lists.begin() + i);
1049         return true;
1050 }
1051
1052 const InventoryList * Inventory::getList(const std::string &name) const
1053 {
1054         s32 i = getListIndex(name);
1055         if(i == -1)
1056                 return NULL;
1057         return m_lists[i];
1058 }
1059
1060 const s32 Inventory::getListIndex(const std::string &name) const
1061 {
1062         for(u32 i=0; i<m_lists.size(); i++)
1063         {
1064                 if(m_lists[i]->getName() == name)
1065                         return i;
1066         }
1067         return -1;
1068 }
1069
1070 //END