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