Commented out debug statements again
[oweals/minetest.git] / src / inventory.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 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 /*
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
22 */
23
24 #include "inventory.h"
25 #include "serialization.h"
26 #include "utility.h"
27 #include "debug.h"
28 #include <sstream>
29 #include "main.h"
30 #include "serverobject.h"
31 #include "content_mapnode.h"
32 #include "content_inventory.h"
33 #include "content_sao.h"
34 #include "player.h"
35
36 /*
37         InventoryItem
38 */
39
40 InventoryItem::InventoryItem(u16 count)
41 {
42         m_count = count;
43 }
44
45 InventoryItem::~InventoryItem()
46 {
47 }
48
49 content_t content_translate_from_19_to_internal(content_t c_from)
50 {
51         for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
52         {
53                 if(trans_table_19[i][1] == c_from)
54                 {
55                         return trans_table_19[i][0];
56                 }
57         }
58         return c_from;
59 }
60
61 InventoryItem* InventoryItem::deSerialize(std::istream &is)
62 {
63         DSTACK(__FUNCTION_NAME);
64
65         //is.imbue(std::locale("C"));
66         // Read name
67         std::string name;
68         std::getline(is, name, ' ');
69         
70         if(name == "MaterialItem")
71         {
72                 // u16 reads directly as a number (u8 doesn't)
73                 u16 material;
74                 is>>material;
75                 u16 count;
76                 is>>count;
77                 // Convert old materials
78                 if(material <= 0xff)
79                 {
80                         material = content_translate_from_19_to_internal(material);
81                 }
82                 if(material > MAX_CONTENT)
83                         throw SerializationError("Too large material number");
84                 return new MaterialItem(material, count);
85         }
86         else if(name == "MaterialItem2")
87         {
88                 u16 material;
89                 is>>material;
90                 u16 count;
91                 is>>count;
92                 if(material > MAX_CONTENT)
93                         throw SerializationError("Too large material number");
94                 return new MaterialItem(material, count);
95         }
96         else if(name == "MBOItem")
97         {
98                 std::string inventorystring;
99                 std::getline(is, inventorystring, '|');
100                 return new MapBlockObjectItem(inventorystring);
101         }
102         else if(name == "CraftItem")
103         {
104                 std::string subname;
105                 std::getline(is, subname, ' ');
106                 u16 count;
107                 is>>count;
108                 return new CraftItem(subname, count);
109         }
110         else if(name == "ToolItem")
111         {
112                 std::string toolname;
113                 std::getline(is, toolname, ' ');
114                 u16 wear;
115                 is>>wear;
116                 return new ToolItem(toolname, wear);
117         }
118         else
119         {
120                 dstream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
121                 throw SerializationError("Unknown InventoryItem name");
122         }
123 }
124
125 std::string InventoryItem::getItemString() {
126         // Get item string
127         std::ostringstream os(std::ios_base::binary);
128         serialize(os);
129         return os.str();
130 }
131
132 ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
133 {
134         /*
135                 Create an ItemSAO
136         */
137         // Create object
138         ServerActiveObject *obj = new ItemSAO(env, 0, pos, getItemString());
139         return obj;
140 }
141
142 /*
143         MaterialItem
144 */
145
146 bool MaterialItem::isCookable() const
147 {
148         return item_material_is_cookable(m_content);
149 }
150
151 InventoryItem *MaterialItem::createCookResult() const
152 {
153         return item_material_create_cook_result(m_content);
154 }
155
156 /*
157         CraftItem
158 */
159
160 #ifndef SERVER
161 video::ITexture * CraftItem::getImage()
162 {
163         if(g_texturesource == NULL)
164                 return NULL;
165         
166         std::string name = item_craft_get_image_name(m_subname);
167
168         // Get such a texture
169         return g_texturesource->getTextureRaw(name);
170 }
171 #endif
172
173 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
174 {
175         // Special cases
176         ServerActiveObject *obj = item_craft_create_object(m_subname, env, id, pos);
177         if(obj)
178                 return obj;
179         // Default
180         return InventoryItem::createSAO(env, id, pos);
181 }
182
183 u16 CraftItem::getDropCount() const
184 {
185         // Special cases
186         s16 dc = item_craft_get_drop_count(m_subname);
187         if(dc != -1)
188                 return dc;
189         // Default
190         return InventoryItem::getDropCount();
191 }
192
193 bool CraftItem::isCookable() const
194 {
195         return item_craft_is_cookable(m_subname);
196 }
197
198 InventoryItem *CraftItem::createCookResult() const
199 {
200         return item_craft_create_cook_result(m_subname);
201 }
202
203 bool CraftItem::use(ServerEnvironment *env, Player *player)
204 {
205         if(item_craft_is_eatable(m_subname))
206         {
207                 u16 result_count = getCount() - 1; // Eat one at a time
208                 s16 hp_change = item_craft_eat_hp_change(m_subname);
209                 if(player->hp + hp_change > 20)
210                         player->hp = 20;
211                 else
212                         player->hp += hp_change;
213                 
214                 if(result_count < 1)
215                         return true;
216                 else
217                         setCount(result_count);
218         }
219         return false;
220 }
221
222 /*
223         MapBlockObjectItem DEPRECATED
224         TODO: Remove
225 */
226 #ifndef SERVER
227 video::ITexture * MapBlockObjectItem::getImage()
228 {
229         if(m_inventorystring.substr(0,3) == "Rat")
230                 return g_texturesource->getTextureRaw("rat.png");
231         
232         if(m_inventorystring.substr(0,4) == "Sign")
233                 return g_texturesource->getTextureRaw("sign.png");
234
235         return NULL;
236 }
237 #endif
238 std::string MapBlockObjectItem::getText()
239 {
240         if(m_inventorystring.substr(0,3) == "Rat")
241                 return "";
242         
243         if(m_inventorystring.substr(0,4) == "Sign")
244                 return "";
245
246         return "obj";
247 }
248
249 MapBlockObject * MapBlockObjectItem::createObject
250                 (v3f pos, f32 player_yaw, f32 player_pitch)
251 {
252         std::istringstream is(m_inventorystring);
253         std::string name;
254         std::getline(is, name, ' ');
255         
256         if(name == "None")
257         {
258                 return NULL;
259         }
260         else if(name == "Sign")
261         {
262                 std::string text;
263                 std::getline(is, text, '|');
264                 SignObject *obj = new SignObject(NULL, -1, pos);
265                 obj->setText(text);
266                 obj->setYaw(-player_yaw);
267                 return obj;
268         }
269         else if(name == "Rat")
270         {
271                 RatObject *obj = new RatObject(NULL, -1, pos);
272                 return obj;
273         }
274         else if(name == "ItemObj")
275         {
276                 /*
277                         Now we are an inventory item containing the serialization
278                         string of an object that contains the serialization
279                         string of an inventory item. Fuck this.
280                 */
281                 //assert(0);
282                 dstream<<__FUNCTION_NAME<<": WARNING: Ignoring ItemObj "
283                                 <<"because an item-object should never be inside "
284                                 <<"an object-item."<<std::endl;
285                 return NULL;
286         }
287         else
288         {
289                 return NULL;
290         }
291 }
292
293 /*
294         Inventory
295 */
296
297 InventoryList::InventoryList(std::string name, u32 size)
298 {
299         m_name = name;
300         m_size = size;
301         clearItems();
302         //m_dirty = false;
303 }
304
305 InventoryList::~InventoryList()
306 {
307         for(u32 i=0; i<m_items.size(); i++)
308         {
309                 if(m_items[i])
310                         delete m_items[i];
311         }
312 }
313
314 void InventoryList::clearItems()
315 {
316         for(u32 i=0; i<m_items.size(); i++)
317         {
318                 if(m_items[i])
319                         delete m_items[i];
320         }
321
322         m_items.clear();
323
324         for(u32 i=0; i<m_size; i++)
325         {
326                 m_items.push_back(NULL);
327         }
328
329         //setDirty(true);
330 }
331
332 void InventoryList::serialize(std::ostream &os) const
333 {
334         //os.imbue(std::locale("C"));
335         
336         for(u32 i=0; i<m_items.size(); i++)
337         {
338                 InventoryItem *item = m_items[i];
339                 if(item != NULL)
340                 {
341                         os<<"Item ";
342                         item->serialize(os);
343                 }
344                 else
345                 {
346                         os<<"Empty";
347                 }
348                 os<<"\n";
349         }
350
351         os<<"EndInventoryList\n";
352 }
353
354 void InventoryList::deSerialize(std::istream &is)
355 {
356         //is.imbue(std::locale("C"));
357
358         clearItems();
359         u32 item_i = 0;
360
361         for(;;)
362         {
363                 std::string line;
364                 std::getline(is, line, '\n');
365
366                 std::istringstream iss(line);
367                 //iss.imbue(std::locale("C"));
368
369                 std::string name;
370                 std::getline(iss, name, ' ');
371
372                 if(name == "EndInventoryList")
373                 {
374                         break;
375                 }
376                 // This is a temporary backwards compatibility fix
377                 else if(name == "end")
378                 {
379                         break;
380                 }
381                 else if(name == "Item")
382                 {
383                         if(item_i > getSize() - 1)
384                                 throw SerializationError("too many items");
385                         InventoryItem *item = InventoryItem::deSerialize(iss);
386                         m_items[item_i++] = item;
387                 }
388                 else if(name == "Empty")
389                 {
390                         if(item_i > getSize() - 1)
391                                 throw SerializationError("too many items");
392                         m_items[item_i++] = NULL;
393                 }
394                 else
395                 {
396                         throw SerializationError("Unknown inventory identifier");
397                 }
398         }
399 }
400
401 InventoryList::InventoryList(const InventoryList &other)
402 {
403         /*
404                 Do this so that the items get cloned. Otherwise the pointers
405                 in the array will just get copied.
406         */
407         *this = other;
408 }
409
410 InventoryList & InventoryList::operator = (const InventoryList &other)
411 {
412         m_name = other.m_name;
413         m_size = other.m_size;
414         clearItems();
415         for(u32 i=0; i<other.m_items.size(); i++)
416         {
417                 InventoryItem *item = other.m_items[i];
418                 if(item != NULL)
419                 {
420                         m_items[i] = item->clone();
421                 }
422         }
423         //setDirty(true);
424
425         return *this;
426 }
427
428 const std::string &InventoryList::getName() const
429 {
430         return m_name;
431 }
432
433 u32 InventoryList::getSize()
434 {
435         return m_items.size();
436 }
437
438 u32 InventoryList::getUsedSlots()
439 {
440         u32 num = 0;
441         for(u32 i=0; i<m_items.size(); i++)
442         {
443                 InventoryItem *item = m_items[i];
444                 if(item != NULL)
445                         num++;
446         }
447         return num;
448 }
449
450 u32 InventoryList::getFreeSlots()
451 {
452         return getSize() - getUsedSlots();
453 }
454
455 const InventoryItem * InventoryList::getItem(u32 i) const
456 {
457         if(i > m_items.size() - 1)
458                 return NULL;
459         return m_items[i];
460 }
461
462 InventoryItem * InventoryList::getItem(u32 i)
463 {
464         if(i > m_items.size() - 1)
465                 return NULL;
466         return m_items[i];
467 }
468
469 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
470 {
471         assert(i < m_items.size());
472
473         InventoryItem *olditem = m_items[i];
474         m_items[i] = newitem;
475         //setDirty(true);
476         return olditem;
477 }
478
479 void InventoryList::deleteItem(u32 i)
480 {
481         assert(i < m_items.size());
482         InventoryItem *item = changeItem(i, NULL);
483         if(item)
484                 delete item;
485 }
486
487 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
488 {
489         if(newitem == NULL)
490                 return NULL;
491         
492         /*
493                 First try to find if it could be added to some existing items
494         */
495         for(u32 i=0; i<m_items.size(); i++)
496         {
497                 // Ignore empty slots
498                 if(m_items[i] == NULL)
499                         continue;
500                 // Try adding
501                 newitem = addItem(i, newitem);
502                 if(newitem == NULL)
503                         return NULL; // All was eaten
504         }
505
506         /*
507                 Then try to add it to empty slots
508         */
509         for(u32 i=0; i<m_items.size(); i++)
510         {
511                 // Ignore unempty slots
512                 if(m_items[i] != NULL)
513                         continue;
514                 // Try adding
515                 newitem = addItem(i, newitem);
516                 if(newitem == NULL)
517                         return NULL; // All was eaten
518         }
519
520         // Return leftover
521         return newitem;
522 }
523
524 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
525 {
526         if(newitem == NULL)
527                 return NULL;
528         
529         //setDirty(true);
530         
531         // If it is an empty position, it's an easy job.
532         InventoryItem *to_item = getItem(i);
533         if(to_item == NULL)
534         {
535                 m_items[i] = newitem;
536                 return NULL;
537         }
538         
539         // If not addable, return the item
540         if(newitem->addableTo(to_item) == false)
541                 return newitem;
542         
543         // If the item fits fully in the slot, add counter and delete it
544         if(newitem->getCount() <= to_item->freeSpace())
545         {
546                 to_item->add(newitem->getCount());
547                 delete newitem;
548                 return NULL;
549         }
550         // Else the item does not fit fully. Add all that fits and return
551         // the rest.
552         else
553         {
554                 u16 freespace = to_item->freeSpace();
555                 to_item->add(freespace);
556                 newitem->remove(freespace);
557                 return newitem;
558         }
559 }
560
561 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
562 {
563         // If it is an empty position, it's an easy job.
564         const InventoryItem *to_item = getItem(i);
565         if(to_item == NULL)
566         {
567                 return true;
568         }
569         
570         // If not addable, fail
571         if(newitem->addableTo(to_item) == false)
572                 return false;
573         
574         // If the item fits fully in the slot, pass
575         if(newitem->getCount() <= to_item->freeSpace())
576         {
577                 return true;
578         }
579
580         return false;
581 }
582
583 bool InventoryList::roomForItem(const InventoryItem *item)
584 {
585         for(u32 i=0; i<m_items.size(); i++)
586                 if(itemFits(i, item))
587                         return true;
588         return false;
589 }
590
591 bool InventoryList::roomForCookedItem(const InventoryItem *item)
592 {
593         if(!item)
594                 return false;
595         const InventoryItem *cook = item->createCookResult();
596         if(!cook)
597                 return false;
598         bool room = roomForItem(cook);
599         delete cook;
600         return room;
601 }
602
603 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
604 {
605         if(count == 0)
606                 return NULL;
607         
608         //setDirty(true);
609
610         InventoryItem *item = getItem(i);
611         // If it is an empty position, return NULL
612         if(item == NULL)
613                 return NULL;
614         
615         if(count >= item->getCount())
616         {
617                 // Get the item by swapping NULL to its place
618                 return changeItem(i, NULL);
619         }
620         else
621         {
622                 InventoryItem *item2 = item->clone();
623                 item->remove(count);
624                 item2->setCount(count);
625                 return item2;
626         }
627         
628         return false;
629 }
630
631 void InventoryList::decrementMaterials(u16 count)
632 {
633         for(u32 i=0; i<m_items.size(); i++)
634         {
635                 InventoryItem *item = takeItem(i, count);
636                 if(item)
637                         delete item;
638         }
639 }
640
641 void InventoryList::print(std::ostream &o)
642 {
643         o<<"InventoryList:"<<std::endl;
644         for(u32 i=0; i<m_items.size(); i++)
645         {
646                 InventoryItem *item = m_items[i];
647                 if(item != NULL)
648                 {
649                         o<<i<<": ";
650                         item->serialize(o);
651                         o<<"\n";
652                 }
653         }
654 }
655
656 /*
657         Inventory
658 */
659
660 Inventory::~Inventory()
661 {
662         clear();
663 }
664
665 void Inventory::clear()
666 {
667         for(u32 i=0; i<m_lists.size(); i++)
668         {
669                 delete m_lists[i];
670         }
671         m_lists.clear();
672 }
673
674 Inventory::Inventory()
675 {
676 }
677
678 Inventory::Inventory(const Inventory &other)
679 {
680         *this = other;
681 }
682
683 Inventory & Inventory::operator = (const Inventory &other)
684 {
685         clear();
686         for(u32 i=0; i<other.m_lists.size(); i++)
687         {
688                 m_lists.push_back(new InventoryList(*other.m_lists[i]));
689         }
690         return *this;
691 }
692
693 void Inventory::serialize(std::ostream &os) const
694 {
695         for(u32 i=0; i<m_lists.size(); i++)
696         {
697                 InventoryList *list = m_lists[i];
698                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
699                 list->serialize(os);
700         }
701
702         os<<"EndInventory\n";
703 }
704
705 void Inventory::deSerialize(std::istream &is)
706 {
707         clear();
708
709         for(;;)
710         {
711                 std::string line;
712                 std::getline(is, line, '\n');
713
714                 std::istringstream iss(line);
715
716                 std::string name;
717                 std::getline(iss, name, ' ');
718
719                 if(name == "EndInventory")
720                 {
721                         break;
722                 }
723                 // This is a temporary backwards compatibility fix
724                 else if(name == "end")
725                 {
726                         break;
727                 }
728                 else if(name == "List")
729                 {
730                         std::string listname;
731                         u32 listsize;
732
733                         std::getline(iss, listname, ' ');
734                         iss>>listsize;
735
736                         InventoryList *list = new InventoryList(listname, listsize);
737                         list->deSerialize(is);
738
739                         m_lists.push_back(list);
740                 }
741                 else
742                 {
743                         throw SerializationError("Unknown inventory identifier");
744                 }
745         }
746 }
747
748 InventoryList * Inventory::addList(const std::string &name, u32 size)
749 {
750         s32 i = getListIndex(name);
751         if(i != -1)
752         {
753                 if(m_lists[i]->getSize() != size)
754                 {
755                         delete m_lists[i];
756                         m_lists[i] = new InventoryList(name, size);
757                 }
758                 return m_lists[i];
759         }
760         else
761         {
762                 m_lists.push_back(new InventoryList(name, size));
763                 return m_lists.getLast();
764         }
765 }
766
767 InventoryList * Inventory::getList(const std::string &name)
768 {
769         s32 i = getListIndex(name);
770         if(i == -1)
771                 return NULL;
772         return m_lists[i];
773 }
774
775 const InventoryList * Inventory::getList(const std::string &name) const
776 {
777         s32 i = getListIndex(name);
778         if(i == -1)
779                 return NULL;
780         return m_lists[i];
781 }
782
783 const s32 Inventory::getListIndex(const std::string &name) const
784 {
785         for(u32 i=0; i<m_lists.size(); i++)
786         {
787                 if(m_lists[i]->getName() == name)
788                         return i;
789         }
790         return -1;
791 }
792
793 /*
794         InventoryAction
795 */
796
797 InventoryAction * InventoryAction::deSerialize(std::istream &is)
798 {
799         std::string type;
800         std::getline(is, type, ' ');
801
802         InventoryAction *a = NULL;
803
804         if(type == "Move")
805         {
806                 a = new IMoveAction(is);
807         }
808
809         return a;
810 }
811
812 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr)
813 {
814 #if 1
815
816         /*dstream<<"from_inv="<<from_inv<<" to_inv="<<to_inv<<std::endl;
817         dstream<<"from_list="<<from_list<<" to_list="<<to_list<<std::endl;
818         dstream<<"from_i="<<from_i<<" to_i="<<to_i<<std::endl;*/
819
820         Inventory *inv_from = mgr->getInventory(c, from_inv);
821         Inventory *inv_to = mgr->getInventory(c, to_inv);
822
823         if(!inv_from || !inv_to)
824         {
825                 dstream<<__FUNCTION_NAME<<": Operation not allowed "
826                                 <<"(inventories not found)"<<std::endl;
827                 return;
828         }
829
830         InventoryList *list_from = inv_from->getList(from_list);
831         InventoryList *list_to = inv_to->getList(to_list);
832
833         /*dstream<<"list_from="<<list_from<<" list_to="<<list_to
834                         <<std::endl;*/
835         /*if(list_from)
836                 dstream<<" list_from->getItem(from_i)="<<list_from->getItem(from_i)
837                                 <<std::endl;
838         if(list_to)
839                 dstream<<" list_to->getItem(to_i)="<<list_to->getItem(to_i)
840                                 <<std::endl;*/
841         
842         /*
843                 If a list doesn't exist or the source item doesn't exist
844         */
845         if(!list_from || !list_to)
846         {
847                 dstream<<__FUNCTION_NAME<<": Operation not allowed "
848                                 <<"(a list doesn't exist)"
849                                 <<std::endl;
850                 return;
851         }
852         if(list_from->getItem(from_i) == NULL)
853         {
854                 dstream<<__FUNCTION_NAME<<": Operation not allowed "
855                                 <<"(the source item doesn't exist)"
856                                 <<std::endl;
857                 return;
858         }
859         /*
860                 If the source and the destination slots are the same
861         */
862         if(inv_from == inv_to && list_from == list_to && from_i == to_i)
863         {
864                 dstream<<__FUNCTION_NAME<<": Operation not allowed "
865                                 <<"(source and the destination slots are the same)"<<std::endl;
866                 return;
867         }
868         
869         // Take item from source list
870         InventoryItem *item1 = NULL;
871         if(count == 0)
872                 item1 = list_from->changeItem(from_i, NULL);
873         else
874                 item1 = list_from->takeItem(from_i, count);
875
876         // Try to add the item to destination list
877         InventoryItem *olditem = item1;
878         item1 = list_to->addItem(to_i, item1);
879
880         // If something is returned, the item was not fully added
881         if(item1 != NULL)
882         {
883                 // If olditem is returned, nothing was added.
884                 bool nothing_added = (item1 == olditem);
885                 
886                 // If something else is returned, part of the item was left unadded.
887                 // Add the other part back to the source item
888                 list_from->addItem(from_i, item1);
889
890                 // If olditem is returned, nothing was added.
891                 // Swap the items
892                 if(nothing_added)
893                 {
894                         // Take item from source list
895                         item1 = list_from->changeItem(from_i, NULL);
896                         // Adding was not possible, swap the items.
897                         InventoryItem *item2 = list_to->changeItem(to_i, item1);
898                         // Put item from destination list to the source list
899                         list_from->changeItem(from_i, item2);
900                 }
901         }
902
903         mgr->inventoryModified(c, from_inv);
904         if(from_inv != to_inv)
905                 mgr->inventoryModified(c, to_inv);
906 #endif
907 }
908
909 /*
910         Craft checking system
911 */
912
913 bool ItemSpec::checkItem(const InventoryItem *item) const
914 {
915         if(type == ITEM_NONE)
916         {
917                 // Has to be no item
918                 if(item != NULL)
919                         return false;
920                 return true;
921         }
922         
923         // There should be an item
924         if(item == NULL)
925                 return false;
926
927         std::string itemname = item->getName();
928
929         if(type == ITEM_MATERIAL)
930         {
931                 if(itemname != "MaterialItem")
932                         return false;
933                 MaterialItem *mitem = (MaterialItem*)item;
934                 if(mitem->getMaterial() != num)
935                         return false;
936         }
937         else if(type == ITEM_CRAFT)
938         {
939                 if(itemname != "CraftItem")
940                         return false;
941                 CraftItem *mitem = (CraftItem*)item;
942                 if(mitem->getSubName() != name)
943                         return false;
944         }
945         else if(type == ITEM_TOOL)
946         {
947                 // Not supported yet
948                 assert(0);
949         }
950         else if(type == ITEM_MBO)
951         {
952                 // Not supported yet
953                 assert(0);
954         }
955         else
956         {
957                 // Not supported yet
958                 assert(0);
959         }
960         return true;
961 }
962
963 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
964 {
965         u16 items_min_x = 100;
966         u16 items_max_x = 100;
967         u16 items_min_y = 100;
968         u16 items_max_y = 100;
969         for(u16 y=0; y<3; y++)
970         for(u16 x=0; x<3; x++)
971         {
972                 if(items[y*3 + x] == NULL)
973                         continue;
974                 if(items_min_x == 100 || x < items_min_x)
975                         items_min_x = x;
976                 if(items_min_y == 100 || y < items_min_y)
977                         items_min_y = y;
978                 if(items_max_x == 100 || x > items_max_x)
979                         items_max_x = x;
980                 if(items_max_y == 100 || y > items_max_y)
981                         items_max_y = y;
982         }
983         // No items at all, just return false
984         if(items_min_x == 100)
985                 return false;
986         
987         u16 items_w = items_max_x - items_min_x + 1;
988         u16 items_h = items_max_y - items_min_y + 1;
989
990         u16 specs_min_x = 100;
991         u16 specs_max_x = 100;
992         u16 specs_min_y = 100;
993         u16 specs_max_y = 100;
994         for(u16 y=0; y<3; y++)
995         for(u16 x=0; x<3; x++)
996         {
997                 if(specs[y*3 + x].type == ITEM_NONE)
998                         continue;
999                 if(specs_min_x == 100 || x < specs_min_x)
1000                         specs_min_x = x;
1001                 if(specs_min_y == 100 || y < specs_min_y)
1002                         specs_min_y = y;
1003                 if(specs_max_x == 100 || x > specs_max_x)
1004                         specs_max_x = x;
1005                 if(specs_max_y == 100 || y > specs_max_y)
1006                         specs_max_y = y;
1007         }
1008         // No specs at all, just return false
1009         if(specs_min_x == 100)
1010                 return false;
1011
1012         u16 specs_w = specs_max_x - specs_min_x + 1;
1013         u16 specs_h = specs_max_y - specs_min_y + 1;
1014
1015         // Different sizes
1016         if(items_w != specs_w || items_h != specs_h)
1017                 return false;
1018
1019         for(u16 y=0; y<specs_h; y++)
1020         for(u16 x=0; x<specs_w; x++)
1021         {
1022                 u16 items_x = items_min_x + x;
1023                 u16 items_y = items_min_y + y;
1024                 u16 specs_x = specs_min_x + x;
1025                 u16 specs_y = specs_min_y + y;
1026                 const InventoryItem *item = items[items_y * 3 + items_x];
1027                 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1028
1029                 if(spec.checkItem(item) == false)
1030                         return false;
1031         }
1032
1033         return true;
1034 }
1035         
1036 //END