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