Remove deprecated code segments (#5891)
[oweals/minetest.git] / src / inventory.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "inventory.h"
21 #include "serialization.h"
22 #include "debug.h"
23 #include <sstream>
24 #include "log.h"
25 #include "itemdef.h"
26 #include "util/strfnd.h"
27 #include "util/serialize.h"
28 #include "util/string.h"
29
30 /*
31         ItemStack
32 */
33
34 ItemStack::ItemStack(const std::string &name_, u16 count_,
35                 u16 wear_, IItemDefManager *itemdef) :
36         name(itemdef->getAlias(name_)),
37         count(count_),
38         wear(wear_)
39 {
40         if (name.empty() || count == 0)
41                 clear();
42         else if (itemdef->get(name).type == ITEM_TOOL)
43                 count = 1;
44 }
45
46 void ItemStack::serialize(std::ostream &os) const
47 {
48         DSTACK(FUNCTION_NAME);
49
50         if(empty())
51                 return;
52
53         // Check how many parts of the itemstring are needed
54         int parts = 1;
55         if(count != 1)
56                 parts = 2;
57         if(wear != 0)
58                 parts = 3;
59         if (!metadata.empty())
60                 parts = 4;
61
62         os<<serializeJsonStringIfNeeded(name);
63         if(parts >= 2)
64                 os<<" "<<count;
65         if(parts >= 3)
66                 os<<" "<<wear;
67         if (parts >= 4) {
68                 os << " ";
69                 metadata.serialize(os);
70         }
71 }
72
73 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
74 {
75         DSTACK(FUNCTION_NAME);
76
77         clear();
78
79         // Read name
80         name = deSerializeJsonStringIfNeeded(is);
81
82         // Skip space
83         std::string tmp;
84         std::getline(is, tmp, ' ');
85         if(!tmp.empty())
86                 throw SerializationError("Unexpected text after item name");
87
88         do { // This loop is just to allow "break;"
89                 // The real thing
90
91                 // Apply item aliases
92                 if (itemdef)
93                         name = itemdef->getAlias(name);
94
95                 // Read the count
96                 std::string count_str;
97                 std::getline(is, count_str, ' ');
98                 if (count_str.empty()) {
99                         count = 1;
100                         break;
101                 } else {
102                         count = stoi(count_str);
103                 }
104
105                 // Read the wear
106                 std::string wear_str;
107                 std::getline(is, wear_str, ' ');
108                 if (wear_str.empty())
109                         break;
110                 else
111                         wear = stoi(wear_str);
112
113                 // Read metadata
114                 metadata.deSerialize(is);
115
116         } while(false);
117
118         if (name.empty() || count == 0)
119                 clear();
120         else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
121                 count = 1;
122 }
123
124 void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
125 {
126         std::istringstream is(str, std::ios::binary);
127         deSerialize(is, itemdef);
128 }
129
130 std::string ItemStack::getItemString() const
131 {
132         std::ostringstream os(std::ios::binary);
133         serialize(os);
134         return os.str();
135 }
136
137
138 ItemStack ItemStack::addItem(const ItemStack &newitem_,
139                 IItemDefManager *itemdef)
140 {
141         ItemStack newitem = newitem_;
142
143         // If the item is empty or the position invalid, bail out
144         if(newitem.empty())
145         {
146                 // nothing can be added trivially
147         }
148         // If this is an empty item, it's an easy job.
149         else if(empty())
150         {
151                 *this = newitem;
152                 newitem.clear();
153         }
154         // If item name or metadata differs, bail out
155         else if (name != newitem.name
156                 || metadata != newitem.metadata)
157         {
158                 // cannot be added
159         }
160         // If the item fits fully, add counter and delete it
161         else if(newitem.count <= freeSpace(itemdef))
162         {
163                 add(newitem.count);
164                 newitem.clear();
165         }
166         // Else the item does not fit fully. Add all that fits and return
167         // the rest.
168         else
169         {
170                 u16 freespace = freeSpace(itemdef);
171                 add(freespace);
172                 newitem.remove(freespace);
173         }
174
175         return newitem;
176 }
177
178 bool ItemStack::itemFits(const ItemStack &newitem_,
179                 ItemStack *restitem,
180                 IItemDefManager *itemdef) const
181 {
182         ItemStack newitem = newitem_;
183
184         // If the item is empty or the position invalid, bail out
185         if(newitem.empty())
186         {
187                 // nothing can be added trivially
188         }
189         // If this is an empty item, it's an easy job.
190         else if(empty())
191         {
192                 newitem.clear();
193         }
194         // If item name or metadata differs, bail out
195         else if (name != newitem.name
196                 || metadata != newitem.metadata)
197         {
198                 // cannot be added
199         }
200         // If the item fits fully, delete it
201         else if(newitem.count <= freeSpace(itemdef))
202         {
203                 newitem.clear();
204         }
205         // Else the item does not fit fully. Return the rest.
206         // the rest.
207         else
208         {
209                 u16 freespace = freeSpace(itemdef);
210                 newitem.remove(freespace);
211         }
212
213         if(restitem)
214                 *restitem = newitem;
215         return newitem.empty();
216 }
217
218 ItemStack ItemStack::takeItem(u32 takecount)
219 {
220         if(takecount == 0 || count == 0)
221                 return ItemStack();
222
223         ItemStack result = *this;
224         if(takecount >= count)
225         {
226                 // Take all
227                 clear();
228         }
229         else
230         {
231                 // Take part
232                 remove(takecount);
233                 result.count = takecount;
234         }
235         return result;
236 }
237
238 ItemStack ItemStack::peekItem(u32 peekcount) const
239 {
240         if(peekcount == 0 || count == 0)
241                 return ItemStack();
242
243         ItemStack result = *this;
244         if(peekcount < count)
245                 result.count = peekcount;
246         return result;
247 }
248
249 /*
250         Inventory
251 */
252
253 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
254         m_name(name),
255         m_size(size),
256         m_width(0),
257         m_itemdef(itemdef)
258 {
259         clearItems();
260 }
261
262 InventoryList::~InventoryList()
263 {
264 }
265
266 void InventoryList::clearItems()
267 {
268         m_items.clear();
269
270         for(u32 i=0; i<m_size; i++)
271         {
272                 m_items.push_back(ItemStack());
273         }
274
275         //setDirty(true);
276 }
277
278 void InventoryList::setSize(u32 newsize)
279 {
280         if(newsize != m_items.size())
281                 m_items.resize(newsize);
282         m_size = newsize;
283 }
284
285 void InventoryList::setWidth(u32 newwidth)
286 {
287         m_width = newwidth;
288 }
289
290 void InventoryList::setName(const std::string &name)
291 {
292         m_name = name;
293 }
294
295 void InventoryList::serialize(std::ostream &os) const
296 {
297         //os.imbue(std::locale("C"));
298
299         os<<"Width "<<m_width<<"\n";
300
301         for(u32 i=0; i<m_items.size(); i++)
302         {
303                 const ItemStack &item = m_items[i];
304                 if(item.empty())
305                 {
306                         os<<"Empty";
307                 }
308                 else
309                 {
310                         os<<"Item ";
311                         item.serialize(os);
312                 }
313                 os<<"\n";
314         }
315
316         os<<"EndInventoryList\n";
317 }
318
319 void InventoryList::deSerialize(std::istream &is)
320 {
321         //is.imbue(std::locale("C"));
322
323         clearItems();
324         u32 item_i = 0;
325         m_width = 0;
326
327         for(;;)
328         {
329                 std::string line;
330                 std::getline(is, line, '\n');
331
332                 std::istringstream iss(line);
333                 //iss.imbue(std::locale("C"));
334
335                 std::string name;
336                 std::getline(iss, name, ' ');
337
338                 if(name == "EndInventoryList")
339                 {
340                         break;
341                 }
342                 // This is a temporary backwards compatibility fix
343                 else if(name == "end")
344                 {
345                         break;
346                 }
347                 else if(name == "Width")
348                 {
349                         iss >> m_width;
350                         if (iss.fail())
351                                 throw SerializationError("incorrect width property");
352                 }
353                 else if(name == "Item")
354                 {
355                         if(item_i > getSize() - 1)
356                                 throw SerializationError("too many items");
357                         ItemStack item;
358                         item.deSerialize(iss, m_itemdef);
359                         m_items[item_i++] = item;
360                 }
361                 else if(name == "Empty")
362                 {
363                         if(item_i > getSize() - 1)
364                                 throw SerializationError("too many items");
365                         m_items[item_i++].clear();
366                 }
367         }
368 }
369
370 InventoryList::InventoryList(const InventoryList &other)
371 {
372         *this = other;
373 }
374
375 InventoryList & InventoryList::operator = (const InventoryList &other)
376 {
377         m_items = other.m_items;
378         m_size = other.m_size;
379         m_width = other.m_width;
380         m_name = other.m_name;
381         m_itemdef = other.m_itemdef;
382         //setDirty(true);
383
384         return *this;
385 }
386
387 bool InventoryList::operator == (const InventoryList &other) const
388 {
389         if(m_size != other.m_size)
390                 return false;
391         if(m_width != other.m_width)
392                 return false;
393         if(m_name != other.m_name)
394                 return false;
395         for(u32 i=0; i<m_items.size(); i++)
396         {
397                 ItemStack s1 = m_items[i];
398                 ItemStack s2 = other.m_items[i];
399                 if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
400                                 s1.metadata != s2.metadata)
401                         return false;
402         }
403
404         return true;
405 }
406
407 const std::string &InventoryList::getName() const
408 {
409         return m_name;
410 }
411
412 u32 InventoryList::getSize() const
413 {
414         return m_items.size();
415 }
416
417 u32 InventoryList::getWidth() const
418 {
419         return m_width;
420 }
421
422 u32 InventoryList::getUsedSlots() const
423 {
424         u32 num = 0;
425         for(u32 i=0; i<m_items.size(); i++)
426         {
427                 if(!m_items[i].empty())
428                         num++;
429         }
430         return num;
431 }
432
433 u32 InventoryList::getFreeSlots() const
434 {
435         return getSize() - getUsedSlots();
436 }
437
438 const ItemStack& InventoryList::getItem(u32 i) const
439 {
440         assert(i < m_size); // Pre-condition
441         return m_items[i];
442 }
443
444 ItemStack& InventoryList::getItem(u32 i)
445 {
446         assert(i < m_size); // Pre-condition
447         return m_items[i];
448 }
449
450 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
451 {
452         if(i >= m_items.size())
453                 return newitem;
454
455         ItemStack olditem = m_items[i];
456         m_items[i] = newitem;
457         //setDirty(true);
458         return olditem;
459 }
460
461 void InventoryList::deleteItem(u32 i)
462 {
463         assert(i < m_items.size()); // Pre-condition
464         m_items[i].clear();
465 }
466
467 ItemStack InventoryList::addItem(const ItemStack &newitem_)
468 {
469         ItemStack newitem = newitem_;
470
471         if(newitem.empty())
472                 return newitem;
473
474         /*
475                 First try to find if it could be added to some existing items
476         */
477         for(u32 i=0; i<m_items.size(); i++)
478         {
479                 // Ignore empty slots
480                 if(m_items[i].empty())
481                         continue;
482                 // Try adding
483                 newitem = addItem(i, newitem);
484                 if(newitem.empty())
485                         return newitem; // All was eaten
486         }
487
488         /*
489                 Then try to add it to empty slots
490         */
491         for(u32 i=0; i<m_items.size(); i++)
492         {
493                 // Ignore unempty slots
494                 if(!m_items[i].empty())
495                         continue;
496                 // Try adding
497                 newitem = addItem(i, newitem);
498                 if(newitem.empty())
499                         return newitem; // All was eaten
500         }
501
502         // Return leftover
503         return newitem;
504 }
505
506 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
507 {
508         if(i >= m_items.size())
509                 return newitem;
510
511         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
512         //if(leftover != newitem)
513         //      setDirty(true);
514         return leftover;
515 }
516
517 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
518                 ItemStack *restitem) const
519 {
520         if(i >= m_items.size())
521         {
522                 if(restitem)
523                         *restitem = newitem;
524                 return false;
525         }
526
527         return m_items[i].itemFits(newitem, restitem, m_itemdef);
528 }
529
530 bool InventoryList::roomForItem(const ItemStack &item_) const
531 {
532         ItemStack item = item_;
533         ItemStack leftover;
534         for(u32 i=0; i<m_items.size(); i++)
535         {
536                 if(itemFits(i, item, &leftover))
537                         return true;
538                 item = leftover;
539         }
540         return false;
541 }
542
543 bool InventoryList::containsItem(const ItemStack &item) const
544 {
545         u32 count = item.count;
546         if(count == 0)
547                 return true;
548         for(std::vector<ItemStack>::const_reverse_iterator
549                         i = m_items.rbegin();
550                         i != m_items.rend(); ++i)
551         {
552                 if(count == 0)
553                         break;
554                 if(i->name == item.name)
555                 {
556                         if(i->count >= count)
557                                 return true;
558                         else
559                                 count -= i->count;
560                 }
561         }
562         return false;
563 }
564
565 ItemStack InventoryList::removeItem(const ItemStack &item)
566 {
567         ItemStack removed;
568         for(std::vector<ItemStack>::reverse_iterator
569                         i = m_items.rbegin();
570                         i != m_items.rend(); ++i)
571         {
572                 if(i->name == item.name)
573                 {
574                         u32 still_to_remove = item.count - removed.count;
575                         removed.addItem(i->takeItem(still_to_remove), m_itemdef);
576                         if(removed.count == item.count)
577                                 break;
578                 }
579         }
580         return removed;
581 }
582
583 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
584 {
585         if(i >= m_items.size())
586                 return ItemStack();
587
588         ItemStack taken = m_items[i].takeItem(takecount);
589         //if(!taken.empty())
590         //      setDirty(true);
591         return taken;
592 }
593
594 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
595 {
596         // Take item from source list
597         ItemStack item1;
598         if (count == 0)
599                 item1 = changeItem(i, ItemStack());
600         else
601                 item1 = takeItem(i, count);
602
603         if (item1.empty())
604                 return;
605
606         // Try to add the item to destination list
607         u32 dest_size = dest->getSize();
608         // First try all the non-empty slots
609         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
610                 if (!m_items[dest_i].empty()) {
611                         item1 = dest->addItem(dest_i, item1);
612                         if (item1.empty()) return;
613                 }
614         }
615
616         // Then try all the empty ones
617         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
618                 if (m_items[dest_i].empty()) {
619                         item1 = dest->addItem(dest_i, item1);
620                         if (item1.empty()) return;
621                 }
622         }
623
624         // If we reach this, the item was not fully added
625         // Add the remaining part back to the source item
626         addItem(i, item1);
627 }
628
629 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
630                 u32 count, bool swap_if_needed, bool *did_swap)
631 {
632         if(this == dest && i == dest_i)
633                 return count;
634
635         // Take item from source list
636         ItemStack item1;
637         if(count == 0)
638                 item1 = changeItem(i, ItemStack());
639         else
640                 item1 = takeItem(i, count);
641
642         if(item1.empty())
643                 return 0;
644
645         // Try to add the item to destination list
646         u32 oldcount = item1.count;
647         item1 = dest->addItem(dest_i, item1);
648
649         // If something is returned, the item was not fully added
650         if(!item1.empty())
651         {
652                 // If olditem is returned, nothing was added.
653                 bool nothing_added = (item1.count == oldcount);
654
655                 // If something else is returned, part of the item was left unadded.
656                 // Add the other part back to the source item
657                 addItem(i, item1);
658
659                 // If olditem is returned, nothing was added.
660                 // Swap the items
661                 if (nothing_added && swap_if_needed) {
662                         // Tell that we swapped
663                         if (did_swap != NULL) {
664                                 *did_swap = true;
665                         }
666                         // Take item from source list
667                         item1 = changeItem(i, ItemStack());
668                         // Adding was not possible, swap the items.
669                         ItemStack item2 = dest->changeItem(dest_i, item1);
670                         // Put item from destination list to the source list
671                         changeItem(i, item2);
672                 }
673         }
674         return (oldcount - item1.count);
675 }
676
677 /*
678         Inventory
679 */
680
681 Inventory::~Inventory()
682 {
683         clear();
684 }
685
686 void Inventory::clear()
687 {
688         m_dirty = true;
689         for(u32 i=0; i<m_lists.size(); i++)
690         {
691                 delete m_lists[i];
692         }
693         m_lists.clear();
694 }
695
696 void Inventory::clearContents()
697 {
698         m_dirty = true;
699         for(u32 i=0; i<m_lists.size(); i++)
700         {
701                 InventoryList *list = m_lists[i];
702                 for(u32 j=0; j<list->getSize(); j++)
703                 {
704                         list->deleteItem(j);
705                 }
706         }
707 }
708
709 Inventory::Inventory(IItemDefManager *itemdef)
710 {
711         m_dirty = false;
712         m_itemdef = itemdef;
713 }
714
715 Inventory::Inventory(const Inventory &other)
716 {
717         *this = other;
718         m_dirty = false;
719 }
720
721 Inventory & Inventory::operator = (const Inventory &other)
722 {
723         // Gracefully handle self assignment
724         if(this != &other)
725         {
726                 m_dirty = true;
727                 clear();
728                 m_itemdef = other.m_itemdef;
729                 for(u32 i=0; i<other.m_lists.size(); i++)
730                 {
731                         m_lists.push_back(new InventoryList(*other.m_lists[i]));
732                 }
733         }
734         return *this;
735 }
736
737 bool Inventory::operator == (const Inventory &other) const
738 {
739         if(m_lists.size() != other.m_lists.size())
740                 return false;
741
742         for(u32 i=0; i<m_lists.size(); i++)
743         {
744                 if(*m_lists[i] != *other.m_lists[i])
745                         return false;
746         }
747         return true;
748 }
749
750 void Inventory::serialize(std::ostream &os) const
751 {
752         for(u32 i=0; i<m_lists.size(); i++)
753         {
754                 InventoryList *list = m_lists[i];
755                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
756                 list->serialize(os);
757         }
758
759         os<<"EndInventory\n";
760 }
761
762 void Inventory::deSerialize(std::istream &is)
763 {
764         clear();
765
766         for(;;)
767         {
768                 std::string line;
769                 std::getline(is, line, '\n');
770
771                 std::istringstream iss(line);
772
773                 std::string name;
774                 std::getline(iss, name, ' ');
775
776                 if(name == "EndInventory")
777                 {
778                         break;
779                 }
780                 // This is a temporary backwards compatibility fix
781                 else if(name == "end")
782                 {
783                         break;
784                 }
785                 else if(name == "List")
786                 {
787                         std::string listname;
788                         u32 listsize;
789
790                         std::getline(iss, listname, ' ');
791                         iss>>listsize;
792
793                         InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
794                         list->deSerialize(is);
795
796                         m_lists.push_back(list);
797                 }
798                 else
799                 {
800                         throw SerializationError("invalid inventory specifier: " + name);
801                 }
802         }
803 }
804
805 InventoryList * Inventory::addList(const std::string &name, u32 size)
806 {
807         m_dirty = true;
808         s32 i = getListIndex(name);
809         if(i != -1)
810         {
811                 if(m_lists[i]->getSize() != size)
812                 {
813                         delete m_lists[i];
814                         m_lists[i] = new InventoryList(name, size, m_itemdef);
815                 }
816                 return m_lists[i];
817         }
818         else
819         {
820                 //don't create list with invalid name
821                 if (name.find(" ") != std::string::npos) return NULL;
822
823                 InventoryList *list = new InventoryList(name, size, m_itemdef);
824                 m_lists.push_back(list);
825                 return list;
826         }
827 }
828
829 InventoryList * Inventory::getList(const std::string &name)
830 {
831         s32 i = getListIndex(name);
832         if(i == -1)
833                 return NULL;
834         return m_lists[i];
835 }
836
837 std::vector<const InventoryList*> Inventory::getLists()
838 {
839         std::vector<const InventoryList*> lists;
840         for(u32 i=0; i<m_lists.size(); i++)
841         {
842                 InventoryList *list = m_lists[i];
843                 lists.push_back(list);
844         }
845         return lists;
846 }
847
848 bool Inventory::deleteList(const std::string &name)
849 {
850         s32 i = getListIndex(name);
851         if(i == -1)
852                 return false;
853         m_dirty = true;
854         delete m_lists[i];
855         m_lists.erase(m_lists.begin() + i);
856         return true;
857 }
858
859 const InventoryList * Inventory::getList(const std::string &name) const
860 {
861         s32 i = getListIndex(name);
862         if(i == -1)
863                 return NULL;
864         return m_lists[i];
865 }
866
867 const s32 Inventory::getListIndex(const std::string &name) const
868 {
869         for(u32 i=0; i<m_lists.size(); i++)
870         {
871                 if(m_lists[i]->getName() == name)
872                         return i;
873         }
874         return -1;
875 }
876
877 //END