NodeMetaRef:{to,from}_table and lua_api.txt additions
[oweals/minetest.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::serialize(std::ostream &os) const
462 {
463         //os.imbue(std::locale("C"));
464         
465         for(u32 i=0; i<m_items.size(); i++)
466         {
467                 const ItemStack &item = m_items[i];
468                 if(item.empty())
469                 {
470                         os<<"Empty";
471                 }
472                 else
473                 {
474                         os<<"Item ";
475                         item.serialize(os);
476                 }
477                 os<<"\n";
478         }
479
480         os<<"EndInventoryList\n";
481 }
482
483 void InventoryList::deSerialize(std::istream &is)
484 {
485         //is.imbue(std::locale("C"));
486
487         clearItems();
488         u32 item_i = 0;
489
490         for(;;)
491         {
492                 std::string line;
493                 std::getline(is, line, '\n');
494
495                 std::istringstream iss(line);
496                 //iss.imbue(std::locale("C"));
497
498                 std::string name;
499                 std::getline(iss, name, ' ');
500
501                 if(name == "EndInventoryList")
502                 {
503                         break;
504                 }
505                 // This is a temporary backwards compatibility fix
506                 else if(name == "end")
507                 {
508                         break;
509                 }
510                 else if(name == "Item")
511                 {
512                         if(item_i > getSize() - 1)
513                                 throw SerializationError("too many items");
514                         ItemStack item;
515                         item.deSerialize(iss, m_itemdef);
516                         m_items[item_i++] = item;
517                 }
518                 else if(name == "Empty")
519                 {
520                         if(item_i > getSize() - 1)
521                                 throw SerializationError("too many items");
522                         m_items[item_i++].clear();
523                 }
524                 else
525                 {
526                         throw SerializationError("Unknown inventory identifier");
527                 }
528         }
529 }
530
531 InventoryList::InventoryList(const InventoryList &other)
532 {
533         *this = other;
534 }
535
536 InventoryList & InventoryList::operator = (const InventoryList &other)
537 {
538         m_items = other.m_items;
539         m_size = other.m_size;
540         m_name = other.m_name;
541         m_itemdef = other.m_itemdef;
542         //setDirty(true);
543
544         return *this;
545 }
546
547 const std::string &InventoryList::getName() const
548 {
549         return m_name;
550 }
551
552 u32 InventoryList::getSize() const
553 {
554         return m_items.size();
555 }
556
557 u32 InventoryList::getUsedSlots() const
558 {
559         u32 num = 0;
560         for(u32 i=0; i<m_items.size(); i++)
561         {
562                 if(!m_items[i].empty())
563                         num++;
564         }
565         return num;
566 }
567
568 u32 InventoryList::getFreeSlots() const
569 {
570         return getSize() - getUsedSlots();
571 }
572
573 const ItemStack& InventoryList::getItem(u32 i) const
574 {
575         assert(i < m_size);
576         return m_items[i];
577 }
578
579 ItemStack& InventoryList::getItem(u32 i)
580 {
581         assert(i < m_size);
582         return m_items[i];
583 }
584
585 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
586 {
587         if(i >= m_items.size())
588                 return newitem;
589
590         ItemStack olditem = m_items[i];
591         m_items[i] = newitem;
592         //setDirty(true);
593         return olditem;
594 }
595
596 void InventoryList::deleteItem(u32 i)
597 {
598         assert(i < m_items.size());
599         m_items[i].clear();
600 }
601
602 ItemStack InventoryList::addItem(const ItemStack &newitem_)
603 {
604         ItemStack newitem = newitem_;
605
606         if(newitem.empty())
607                 return newitem;
608         
609         /*
610                 First try to find if it could be added to some existing items
611         */
612         for(u32 i=0; i<m_items.size(); i++)
613         {
614                 // Ignore empty slots
615                 if(m_items[i].empty())
616                         continue;
617                 // Try adding
618                 newitem = addItem(i, newitem);
619                 if(newitem.empty())
620                         return newitem; // All was eaten
621         }
622
623         /*
624                 Then try to add it to empty slots
625         */
626         for(u32 i=0; i<m_items.size(); i++)
627         {
628                 // Ignore unempty slots
629                 if(!m_items[i].empty())
630                         continue;
631                 // Try adding
632                 newitem = addItem(i, newitem);
633                 if(newitem.empty())
634                         return newitem; // All was eaten
635         }
636
637         // Return leftover
638         return newitem;
639 }
640
641 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
642 {
643         if(i >= m_items.size())
644                 return newitem;
645
646         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
647         //if(leftover != newitem)
648         //      setDirty(true);
649         return leftover;
650 }
651
652 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
653                 ItemStack *restitem) const
654 {
655         if(i >= m_items.size())
656         {
657                 if(restitem)
658                         *restitem = newitem;
659                 return false;
660         }
661
662         return m_items[i].itemFits(newitem, restitem, m_itemdef);
663 }
664
665 bool InventoryList::roomForItem(const ItemStack &item_) const
666 {
667         ItemStack item = item_;
668         ItemStack leftover;
669         for(u32 i=0; i<m_items.size(); i++)
670         {
671                 if(itemFits(i, item, &leftover))
672                         return true;
673                 item = leftover;
674         }
675         return false;
676 }
677
678 bool InventoryList::containsItem(const ItemStack &item) const
679 {
680         u32 count = item.count;
681         if(count == 0)
682                 return true;
683         for(std::vector<ItemStack>::const_reverse_iterator
684                         i = m_items.rbegin();
685                         i != m_items.rend(); i++)
686         {
687                 if(count == 0)
688                         break;
689                 if(i->name == item.name)
690                 {
691                         if(i->count >= count)
692                                 return true;
693                         else
694                                 count -= i->count;
695                 }
696         }
697         return false;
698 }
699
700 ItemStack InventoryList::removeItem(const ItemStack &item)
701 {
702         ItemStack removed;
703         for(std::vector<ItemStack>::reverse_iterator
704                         i = m_items.rbegin();
705                         i != m_items.rend(); i++)
706         {
707                 if(i->name == item.name)
708                 {
709                         u32 still_to_remove = item.count - removed.count;
710                         removed.addItem(i->takeItem(still_to_remove), m_itemdef);
711                         if(removed.count == item.count)
712                                 break;
713                 }
714         }
715         return removed;
716 }
717
718 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
719 {
720         if(i >= m_items.size())
721                 return ItemStack();
722
723         ItemStack taken = m_items[i].takeItem(takecount);
724         //if(!taken.empty())
725         //      setDirty(true);
726         return taken;
727 }
728
729 ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
730 {
731         if(i >= m_items.size())
732                 return ItemStack();
733
734         return m_items[i].peekItem(peekcount);
735 }
736
737 void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
738 {
739         if(this == dest && i == dest_i)
740                 return;
741
742         // Take item from source list
743         ItemStack item1;
744         if(count == 0)
745                 item1 = changeItem(i, ItemStack());
746         else
747                 item1 = takeItem(i, count);
748
749         if(item1.empty())
750                 return;
751
752         // Try to add the item to destination list
753         u32 oldcount = item1.count;
754         item1 = dest->addItem(dest_i, item1);
755
756         // If something is returned, the item was not fully added
757         if(!item1.empty())
758         {
759                 // If olditem is returned, nothing was added.
760                 bool nothing_added = (item1.count == oldcount);
761
762                 // If something else is returned, part of the item was left unadded.
763                 // Add the other part back to the source item
764                 addItem(i, item1);
765
766                 // If olditem is returned, nothing was added.
767                 // Swap the items
768                 if(nothing_added)
769                 {
770                         // Take item from source list
771                         item1 = changeItem(i, ItemStack());
772                         // Adding was not possible, swap the items.
773                         ItemStack item2 = dest->changeItem(dest_i, item1);
774                         // Put item from destination list to the source list
775                         changeItem(i, item2);
776                 }
777         }
778 }
779
780 /*
781         Inventory
782 */
783
784 Inventory::~Inventory()
785 {
786         clear();
787 }
788
789 void Inventory::clear()
790 {
791         for(u32 i=0; i<m_lists.size(); i++)
792         {
793                 delete m_lists[i];
794         }
795         m_lists.clear();
796 }
797
798 void Inventory::clearContents()
799 {
800         for(u32 i=0; i<m_lists.size(); i++)
801         {
802                 InventoryList *list = m_lists[i];
803                 for(u32 j=0; j<list->getSize(); j++)
804                 {
805                         list->deleteItem(j);
806                 }
807         }
808 }
809
810 Inventory::Inventory(IItemDefManager *itemdef)
811 {
812         m_itemdef = itemdef;
813 }
814
815 Inventory::Inventory(const Inventory &other)
816 {
817         *this = other;
818 }
819
820 Inventory & Inventory::operator = (const Inventory &other)
821 {
822         // Gracefully handle self assignment
823         if(this != &other)
824         {
825                 clear();
826                 m_itemdef = other.m_itemdef;
827                 for(u32 i=0; i<other.m_lists.size(); i++)
828                 {
829                         m_lists.push_back(new InventoryList(*other.m_lists[i]));
830                 }
831         }
832         return *this;
833 }
834
835 void Inventory::serialize(std::ostream &os) const
836 {
837         for(u32 i=0; i<m_lists.size(); i++)
838         {
839                 InventoryList *list = m_lists[i];
840                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
841                 list->serialize(os);
842         }
843
844         os<<"EndInventory\n";
845 }
846
847 void Inventory::deSerialize(std::istream &is)
848 {
849         clear();
850
851         for(;;)
852         {
853                 std::string line;
854                 std::getline(is, line, '\n');
855
856                 std::istringstream iss(line);
857
858                 std::string name;
859                 std::getline(iss, name, ' ');
860
861                 if(name == "EndInventory")
862                 {
863                         break;
864                 }
865                 // This is a temporary backwards compatibility fix
866                 else if(name == "end")
867                 {
868                         break;
869                 }
870                 else if(name == "List")
871                 {
872                         std::string listname;
873                         u32 listsize;
874
875                         std::getline(iss, listname, ' ');
876                         iss>>listsize;
877
878                         InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
879                         list->deSerialize(is);
880
881                         m_lists.push_back(list);
882                 }
883                 else
884                 {
885                         throw SerializationError("Unknown inventory identifier");
886                 }
887         }
888 }
889
890 InventoryList * Inventory::addList(const std::string &name, u32 size)
891 {
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         else
903         {
904                 InventoryList *list = new InventoryList(name, size, m_itemdef);
905                 m_lists.push_back(list);
906                 return list;
907         }
908 }
909
910 InventoryList * Inventory::getList(const std::string &name)
911 {
912         s32 i = getListIndex(name);
913         if(i == -1)
914                 return NULL;
915         return m_lists[i];
916 }
917
918 std::vector<const InventoryList*> Inventory::getLists()
919 {
920         std::vector<const InventoryList*> lists;
921         for(u32 i=0; i<m_lists.size(); i++)
922         {
923                 InventoryList *list = m_lists[i];
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         delete m_lists[i];
935         m_lists.erase(m_lists.begin() + i);
936         return true;
937 }
938
939 const InventoryList * Inventory::getList(const std::string &name) const
940 {
941         s32 i = getListIndex(name);
942         if(i == -1)
943                 return NULL;
944         return m_lists[i];
945 }
946
947 const s32 Inventory::getListIndex(const std::string &name) const
948 {
949         for(u32 i=0; i<m_lists.size(); i++)
950         {
951                 if(m_lists[i]->getName() == name)
952                         return i;
953         }
954         return -1;
955 }
956
957 //END