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