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