3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "inventory.h"
21 #include "serialization.h"
25 #include "main.h" // For tsrc, g_toolmanager
26 #include "serverobject.h"
27 #include "content_mapnode.h"
28 #include "content_inventory.h"
29 #include "content_sao.h"
30 #include "environment.h"
43 InventoryItem::InventoryItem(IGameDef *gamedef, u16 count):
50 InventoryItem::~InventoryItem()
54 content_t content_translate_from_19_to_internal(content_t c_from)
56 for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
58 if(trans_table_19[i][1] == c_from)
60 return trans_table_19[i][0];
66 InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef)
68 DSTACK(__FUNCTION_NAME);
70 //is.imbue(std::locale("C"));
73 std::getline(is, name, ' ');
75 if(name == "MaterialItem")
77 // u16 reads directly as a number (u8 doesn't)
82 // Convert old materials
84 material = content_translate_from_19_to_internal(material);
85 if(material > MAX_CONTENT)
86 throw SerializationError("Too large material number");
87 return new MaterialItem(gamedef, material, count);
89 else if(name == "MaterialItem2")
95 if(material > MAX_CONTENT)
96 throw SerializationError("Too large material number");
97 return new MaterialItem(gamedef, material, count);
99 else if(name == "NodeItem" || name == "MaterialItem3")
102 std::getline(is, all, '\n');
103 std::string nodename;
104 // First attempt to read inside ""
107 // If didn't skip to end, we have ""s
109 nodename = fnd.next("\"");
110 } else { // No luck, just read a word then
112 nodename = fnd.next(" ");
115 u16 count = stoi(trim(fnd.next("")));
116 return new MaterialItem(gamedef, nodename, count);
118 else if(name == "MBOItem")
120 std::string inventorystring;
121 std::getline(is, inventorystring, '|');
122 throw SerializationError("MBOItem not supported anymore");
124 else if(name == "CraftItem")
127 std::getline(is, all, '\n');
129 // First attempt to read inside ""
132 // If didn't skip to end, we have ""s
134 subname = fnd.next("\"");
135 } else { // No luck, just read a word then
137 subname = fnd.next(" ");
141 u16 count = stoi(trim(fnd.next("")));
142 return new CraftItem(gamedef, subname, count);
144 else if(name == "ToolItem")
147 std::getline(is, all, '\n');
148 std::string toolname;
149 // First attempt to read inside ""
152 // If didn't skip to end, we have ""s
154 toolname = fnd.next("\"");
155 } else { // No luck, just read a word then
157 toolname = fnd.next(" ");
161 u16 wear = stoi(trim(fnd.next("")));
162 return new ToolItem(gamedef, toolname, wear);
166 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
167 throw SerializationError("Unknown InventoryItem name");
171 std::string InventoryItem::getItemString() {
173 std::ostringstream os(std::ios_base::binary);
178 ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, v3f pos)
183 pos.Y -= BS*0.25; // let it drop a bit
185 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
186 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
188 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
196 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
197 InventoryItem(gamedef, count)
200 nodename = "unknown_block";
201 m_nodename = nodename;
203 // Legacy constructor
204 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
205 InventoryItem(gamedef, count)
207 INodeDefManager *ndef = m_gamedef->ndef();
208 std::string nodename = ndef->get(content).name;
210 nodename = "unknown_block";
211 m_nodename = nodename;
215 video::ITexture * MaterialItem::getImage() const
217 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
221 bool MaterialItem::isCookable() const
223 INodeDefManager *ndef = m_gamedef->ndef();
224 const ContentFeatures &f = ndef->get(m_nodename);
225 return (f.cookresult_item != "");
228 InventoryItem *MaterialItem::createCookResult() const
230 INodeDefManager *ndef = m_gamedef->ndef();
231 const ContentFeatures &f = ndef->get(m_nodename);
232 std::istringstream is(f.cookresult_item, std::ios::binary);
233 return InventoryItem::deSerialize(is, m_gamedef);
236 float MaterialItem::getCookTime() const
238 INodeDefManager *ndef = m_gamedef->ndef();
239 const ContentFeatures &f = ndef->get(m_nodename);
240 return f.furnace_cooktime;
243 float MaterialItem::getBurnTime() const
245 INodeDefManager *ndef = m_gamedef->ndef();
246 const ContentFeatures &f = ndef->get(m_nodename);
247 return f.furnace_burntime;
250 content_t MaterialItem::getMaterial() const
252 INodeDefManager *ndef = m_gamedef->ndef();
253 content_t id = CONTENT_IGNORE;
254 ndef->getId(m_nodename, id);
262 std::string ToolItem::getImageBasename() const
264 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
268 video::ITexture * ToolItem::getImage() const
270 ITextureSource *tsrc = m_gamedef->tsrc();
272 std::string basename = getImageBasename();
275 Calculate a progress value with sane amount of
278 u32 maxprogress = 30;
279 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
281 float value_f = (float)toolprogress / (float)maxprogress;
282 std::ostringstream os;
283 os<<basename<<"^[progressbar"<<value_f;
285 return tsrc->getTextureRaw(os.str());
288 video::ITexture * ToolItem::getImageRaw() const
290 ITextureSource *tsrc = m_gamedef->tsrc();
292 return tsrc->getTextureRaw(getImageBasename());
301 video::ITexture * CraftItem::getImage() const
303 ITextureSource *tsrc = m_gamedef->tsrc();
305 std::string name = item_craft_get_image_name(m_subname, m_gamedef);
307 // Get such a texture
308 return tsrc->getTextureRaw(name);
312 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, v3f pos)
315 ServerActiveObject *obj = item_craft_create_object(m_subname, env, pos);
319 return InventoryItem::createSAO(env, pos);
322 u16 CraftItem::getDropCount() const
325 s16 dc = item_craft_get_drop_count(m_subname, m_gamedef);
329 return InventoryItem::getDropCount();
332 bool CraftItem::isCookable() const
334 return item_craft_is_cookable(m_subname, m_gamedef);
337 InventoryItem *CraftItem::createCookResult() const
339 return item_craft_create_cook_result(m_subname, m_gamedef);
342 float CraftItem::getCookTime() const
347 float CraftItem::getBurnTime() const
349 if(m_subname == "lump_of_coal")
354 bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
356 if(!item_craft_is_eatable(m_subname, m_gamedef))
359 u16 result_count = getCount() - 1; // Eat one at a time
360 s16 hp_change = item_craft_eat_hp_change(m_subname, m_gamedef);
361 s16 hp = user->getHP();
370 setCount(result_count);
378 InventoryList::InventoryList(std::string name, u32 size)
386 InventoryList::~InventoryList()
388 for(u32 i=0; i<m_items.size(); i++)
395 void InventoryList::clearItems()
397 for(u32 i=0; i<m_items.size(); i++)
405 for(u32 i=0; i<m_size; i++)
407 m_items.push_back(NULL);
413 void InventoryList::serialize(std::ostream &os) const
415 //os.imbue(std::locale("C"));
417 for(u32 i=0; i<m_items.size(); i++)
419 InventoryItem *item = m_items[i];
432 os<<"EndInventoryList\n";
435 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
437 //is.imbue(std::locale("C"));
445 std::getline(is, line, '\n');
447 std::istringstream iss(line);
448 //iss.imbue(std::locale("C"));
451 std::getline(iss, name, ' ');
453 if(name == "EndInventoryList")
457 // This is a temporary backwards compatibility fix
458 else if(name == "end")
462 else if(name == "Item")
464 if(item_i > getSize() - 1)
465 throw SerializationError("too many items");
466 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
467 m_items[item_i++] = item;
469 else if(name == "Empty")
471 if(item_i > getSize() - 1)
472 throw SerializationError("too many items");
473 m_items[item_i++] = NULL;
477 throw SerializationError("Unknown inventory identifier");
482 InventoryList::InventoryList(const InventoryList &other)
485 Do this so that the items get cloned. Otherwise the pointers
486 in the array will just get copied.
491 InventoryList & InventoryList::operator = (const InventoryList &other)
493 m_name = other.m_name;
494 m_size = other.m_size;
496 for(u32 i=0; i<other.m_items.size(); i++)
498 InventoryItem *item = other.m_items[i];
501 m_items[i] = item->clone();
509 const std::string &InventoryList::getName() const
514 u32 InventoryList::getSize()
516 return m_items.size();
519 u32 InventoryList::getUsedSlots()
522 for(u32 i=0; i<m_items.size(); i++)
524 InventoryItem *item = m_items[i];
531 u32 InventoryList::getFreeSlots()
533 return getSize() - getUsedSlots();
536 const InventoryItem * InventoryList::getItem(u32 i) const
538 if(i > m_items.size() - 1)
543 InventoryItem * InventoryList::getItem(u32 i)
545 if(i > m_items.size() - 1)
550 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
552 assert(i < m_items.size());
554 InventoryItem *olditem = m_items[i];
555 m_items[i] = newitem;
560 void InventoryList::deleteItem(u32 i)
562 assert(i < m_items.size());
563 InventoryItem *item = changeItem(i, NULL);
568 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
574 First try to find if it could be added to some existing items
576 for(u32 i=0; i<m_items.size(); i++)
578 // Ignore empty slots
579 if(m_items[i] == NULL)
582 newitem = addItem(i, newitem);
584 return NULL; // All was eaten
588 Then try to add it to empty slots
590 for(u32 i=0; i<m_items.size(); i++)
592 // Ignore unempty slots
593 if(m_items[i] != NULL)
596 newitem = addItem(i, newitem);
598 return NULL; // All was eaten
605 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
612 // If it is an empty position, it's an easy job.
613 InventoryItem *to_item = getItem(i);
616 m_items[i] = newitem;
620 // If not addable, return the item
621 if(newitem->addableTo(to_item) == false)
624 // If the item fits fully in the slot, add counter and delete it
625 if(newitem->getCount() <= to_item->freeSpace())
627 to_item->add(newitem->getCount());
631 // Else the item does not fit fully. Add all that fits and return
635 u16 freespace = to_item->freeSpace();
636 to_item->add(freespace);
637 newitem->remove(freespace);
642 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
644 // If it is an empty position, it's an easy job.
645 const InventoryItem *to_item = getItem(i);
651 // If not addable, fail
652 if(newitem->addableTo(to_item) == false)
655 // If the item fits fully in the slot, pass
656 if(newitem->getCount() <= to_item->freeSpace())
664 bool InventoryList::roomForItem(const InventoryItem *item)
666 for(u32 i=0; i<m_items.size(); i++)
667 if(itemFits(i, item))
672 bool InventoryList::roomForCookedItem(const InventoryItem *item)
676 const InventoryItem *cook = item->createCookResult();
679 bool room = roomForItem(cook);
684 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
691 InventoryItem *item = getItem(i);
692 // If it is an empty position, return NULL
696 if(count >= item->getCount())
698 // Get the item by swapping NULL to its place
699 return changeItem(i, NULL);
703 InventoryItem *item2 = item->clone();
705 item2->setCount(count);
712 void InventoryList::decrementMaterials(u16 count)
714 for(u32 i=0; i<m_items.size(); i++)
716 InventoryItem *item = takeItem(i, count);
722 void InventoryList::print(std::ostream &o)
724 o<<"InventoryList:"<<std::endl;
725 for(u32 i=0; i<m_items.size(); i++)
727 InventoryItem *item = m_items[i];
741 Inventory::~Inventory()
746 void Inventory::clear()
748 for(u32 i=0; i<m_lists.size(); i++)
755 Inventory::Inventory()
759 Inventory::Inventory(const Inventory &other)
764 Inventory & Inventory::operator = (const Inventory &other)
767 for(u32 i=0; i<other.m_lists.size(); i++)
769 m_lists.push_back(new InventoryList(*other.m_lists[i]));
774 void Inventory::serialize(std::ostream &os) const
776 for(u32 i=0; i<m_lists.size(); i++)
778 InventoryList *list = m_lists[i];
779 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
783 os<<"EndInventory\n";
786 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
793 std::getline(is, line, '\n');
795 std::istringstream iss(line);
798 std::getline(iss, name, ' ');
800 if(name == "EndInventory")
804 // This is a temporary backwards compatibility fix
805 else if(name == "end")
809 else if(name == "List")
811 std::string listname;
814 std::getline(iss, listname, ' ');
817 InventoryList *list = new InventoryList(listname, listsize);
818 list->deSerialize(is, gamedef);
820 m_lists.push_back(list);
824 throw SerializationError("Unknown inventory identifier");
829 InventoryList * Inventory::addList(const std::string &name, u32 size)
831 s32 i = getListIndex(name);
834 if(m_lists[i]->getSize() != size)
837 m_lists[i] = new InventoryList(name, size);
843 m_lists.push_back(new InventoryList(name, size));
844 return m_lists.getLast();
848 InventoryList * Inventory::getList(const std::string &name)
850 s32 i = getListIndex(name);
856 const InventoryList * Inventory::getList(const std::string &name) const
858 s32 i = getListIndex(name);
864 const s32 Inventory::getListIndex(const std::string &name) const
866 for(u32 i=0; i<m_lists.size(); i++)
868 if(m_lists[i]->getName() == name)
878 InventoryAction * InventoryAction::deSerialize(std::istream &is)
881 std::getline(is, type, ' ');
883 InventoryAction *a = NULL;
887 a = new IMoveAction(is);
889 else if(type == "Drop")
891 a = new IDropAction(is);
897 static std::string describeC(const struct InventoryContext *c)
899 if(c->current_player == NULL)
900 return "current_player=NULL";
902 return std::string("current_player=") + c->current_player->getName();
905 IMoveAction::IMoveAction(std::istream &is)
909 std::getline(is, ts, ' ');
912 std::getline(is, from_inv, ' ');
914 std::getline(is, from_list, ' ');
916 std::getline(is, ts, ' ');
919 std::getline(is, to_inv, ' ');
921 std::getline(is, to_list, ' ');
923 std::getline(is, ts, ' ');
927 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
928 ServerEnvironment *env)
930 Inventory *inv_from = mgr->getInventory(c, from_inv);
931 Inventory *inv_to = mgr->getInventory(c, to_inv);
934 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
935 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
936 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
940 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
941 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
942 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
946 InventoryList *list_from = inv_from->getList(from_list);
947 InventoryList *list_to = inv_to->getList(to_list);
950 If a list doesn't exist or the source item doesn't exist
953 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
954 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
955 <<", from_list=\""<<from_list<<"\""<<std::endl;
959 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
960 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
961 <<", to_list=\""<<to_list<<"\""<<std::endl;
964 if(list_from->getItem(from_i) == NULL)
966 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
967 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
968 <<", from_list=\""<<from_list<<"\""
969 <<" from_i="<<from_i<<std::endl;
973 If the source and the destination slots are the same
975 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
977 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
978 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
979 <<"\" i="<<from_i<<std::endl;
983 // Take item from source list
984 InventoryItem *item1 = NULL;
986 item1 = list_from->changeItem(from_i, NULL);
988 item1 = list_from->takeItem(from_i, count);
990 // Try to add the item to destination list
991 InventoryItem *olditem = item1;
992 item1 = list_to->addItem(to_i, item1);
994 // If something is returned, the item was not fully added
997 // If olditem is returned, nothing was added.
998 bool nothing_added = (item1 == olditem);
1000 // If something else is returned, part of the item was left unadded.
1001 // Add the other part back to the source item
1002 list_from->addItem(from_i, item1);
1004 // If olditem is returned, nothing was added.
1008 // Take item from source list
1009 item1 = list_from->changeItem(from_i, NULL);
1010 // Adding was not possible, swap the items.
1011 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1012 // Put item from destination list to the source list
1013 list_from->changeItem(from_i, item2);
1017 mgr->inventoryModified(c, from_inv);
1018 if(from_inv != to_inv)
1019 mgr->inventoryModified(c, to_inv);
1021 infostream<<"IMoveAction::apply(): moved at "
1022 <<"["<<describeC(c)<<"]"
1023 <<" from inv=\""<<from_inv<<"\""
1024 <<" list=\""<<from_list<<"\""
1026 <<" to inv=\""<<to_inv<<"\""
1027 <<" list=\""<<to_list<<"\""
1032 IDropAction::IDropAction(std::istream &is)
1036 std::getline(is, ts, ' ');
1039 std::getline(is, from_inv, ' ');
1041 std::getline(is, from_list, ' ');
1043 std::getline(is, ts, ' ');
1047 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1048 ServerEnvironment *env)
1050 Inventory *inv_from = mgr->getInventory(c, from_inv);
1053 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1054 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1058 InventoryList *list_from = inv_from->getList(from_list);
1061 If a list doesn't exist or the source item doesn't exist
1064 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1065 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1066 <<", from_list=\""<<from_list<<"\""<<std::endl;
1069 if(list_from->getItem(from_i) == NULL)
1071 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1072 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1073 <<", from_list=\""<<from_list<<"\""
1074 <<" from_i="<<from_i<<std::endl;
1078 v3f pos = c->current_player->getPosition();
1080 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
1083 Ensure that the block is loaded so that the item
1084 can properly be added to the static list too
1086 MapBlock *block = env->getMap().emergeBlock(blockpos, false);
1089 infostream<<"IDropAction::apply(): FAIL: block not found: "
1090 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
1095 // Take item from source list
1097 count = list_from->getItem(from_i)->getDropCount();
1098 InventoryItem *item1 = list_from->takeItem(from_i, count);
1100 // Create an active object
1101 ServerActiveObject *obj = item1->createSAO(env, pos);
1104 infostream<<"IDropAction::apply(): item resulted in NULL object, "
1105 <<"not placing onto map"
1110 // Add the object to the environment
1111 env->addActiveObject(obj);
1113 infostream<<"Dropped object"<<std::endl;
1116 mgr->inventoryModified(c, from_inv);
1118 infostream<<"IDropAction::apply(): dropped "
1119 <<"["<<describeC(c)<<"]"
1120 <<" from inv=\""<<from_inv<<"\""
1121 <<" list=\""<<from_list<<"\""
1127 Craft checking system
1130 bool ItemSpec::checkItem(const InventoryItem *item) const
1132 if(type == ITEM_NONE)
1134 // Has to be no item
1140 // There should be an item
1144 std::string itemname = item->getName();
1146 if(type == ITEM_MATERIAL)
1148 if(itemname != "MaterialItem")
1150 MaterialItem *mitem = (MaterialItem*)item;
1152 if(mitem->getMaterial() != num)
1155 if(mitem->getNodeName() != name)
1159 else if(type == ITEM_CRAFT)
1161 if(itemname != "CraftItem")
1163 CraftItem *mitem = (CraftItem*)item;
1164 if(mitem->getSubName() != name)
1167 else if(type == ITEM_TOOL)
1169 // Not supported yet
1172 else if(type == ITEM_MBO)
1174 // Not supported yet
1179 // Not supported yet
1185 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1187 u16 items_min_x = 100;
1188 u16 items_max_x = 100;
1189 u16 items_min_y = 100;
1190 u16 items_max_y = 100;
1191 for(u16 y=0; y<3; y++)
1192 for(u16 x=0; x<3; x++)
1194 if(items[y*3 + x] == NULL)
1196 if(items_min_x == 100 || x < items_min_x)
1198 if(items_min_y == 100 || y < items_min_y)
1200 if(items_max_x == 100 || x > items_max_x)
1202 if(items_max_y == 100 || y > items_max_y)
1205 // No items at all, just return false
1206 if(items_min_x == 100)
1209 u16 items_w = items_max_x - items_min_x + 1;
1210 u16 items_h = items_max_y - items_min_y + 1;
1212 u16 specs_min_x = 100;
1213 u16 specs_max_x = 100;
1214 u16 specs_min_y = 100;
1215 u16 specs_max_y = 100;
1216 for(u16 y=0; y<3; y++)
1217 for(u16 x=0; x<3; x++)
1219 if(specs[y*3 + x].type == ITEM_NONE)
1221 if(specs_min_x == 100 || x < specs_min_x)
1223 if(specs_min_y == 100 || y < specs_min_y)
1225 if(specs_max_x == 100 || x > specs_max_x)
1227 if(specs_max_y == 100 || y > specs_max_y)
1230 // No specs at all, just return false
1231 if(specs_min_x == 100)
1234 u16 specs_w = specs_max_x - specs_min_x + 1;
1235 u16 specs_h = specs_max_y - specs_min_y + 1;
1238 if(items_w != specs_w || items_h != specs_h)
1241 for(u16 y=0; y<specs_h; y++)
1242 for(u16 x=0; x<specs_w; x++)
1244 u16 items_x = items_min_x + x;
1245 u16 items_y = items_min_y + y;
1246 u16 specs_x = specs_min_x + x;
1247 u16 specs_y = specs_min_y + y;
1248 const InventoryItem *item = items[items_y * 3 + items_x];
1249 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1251 if(spec.checkItem(item) == false)
1258 bool checkItemCombination(const InventoryItem * const * items,
1259 const InventoryItem * const * specs)
1261 u16 items_min_x = 100;
1262 u16 items_max_x = 100;
1263 u16 items_min_y = 100;
1264 u16 items_max_y = 100;
1265 for(u16 y=0; y<3; y++)
1266 for(u16 x=0; x<3; x++)
1268 if(items[y*3 + x] == NULL)
1270 if(items_min_x == 100 || x < items_min_x)
1272 if(items_min_y == 100 || y < items_min_y)
1274 if(items_max_x == 100 || x > items_max_x)
1276 if(items_max_y == 100 || y > items_max_y)
1279 // No items at all, just return false
1280 if(items_min_x == 100)
1283 u16 items_w = items_max_x - items_min_x + 1;
1284 u16 items_h = items_max_y - items_min_y + 1;
1286 u16 specs_min_x = 100;
1287 u16 specs_max_x = 100;
1288 u16 specs_min_y = 100;
1289 u16 specs_max_y = 100;
1290 for(u16 y=0; y<3; y++)
1291 for(u16 x=0; x<3; x++)
1293 if(specs[y*3 + x] == NULL)
1295 if(specs_min_x == 100 || x < specs_min_x)
1297 if(specs_min_y == 100 || y < specs_min_y)
1299 if(specs_max_x == 100 || x > specs_max_x)
1301 if(specs_max_y == 100 || y > specs_max_y)
1304 // No specs at all, just return false
1305 if(specs_min_x == 100)
1308 u16 specs_w = specs_max_x - specs_min_x + 1;
1309 u16 specs_h = specs_max_y - specs_min_y + 1;
1312 if(items_w != specs_w || items_h != specs_h)
1315 for(u16 y=0; y<specs_h; y++)
1316 for(u16 x=0; x<specs_w; x++)
1318 u16 items_x = items_min_x + x;
1319 u16 items_y = items_min_y + y;
1320 u16 specs_x = specs_min_x + x;
1321 u16 specs_y = specs_min_y + y;
1322 const InventoryItem *item = items[items_y * 3 + items_x];
1323 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1325 if(item == NULL && spec == NULL)
1327 if(item == NULL && spec != NULL)
1329 if(item != NULL && spec == NULL)
1331 if(!spec->isSubsetOf(item))