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