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