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