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