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