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