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