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 InventoryItem* InventoryItem::deSerialize(const std::string &str,
174 std::istringstream is(str, std::ios_base::binary);
175 return deSerialize(is, gamedef);
178 std::string InventoryItem::getItemString() {
180 std::ostringstream os(std::ios_base::binary);
185 ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, v3f pos)
190 pos.Y -= BS*0.25; // let it drop a bit
192 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
193 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
195 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
203 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
204 InventoryItem(gamedef, count)
207 nodename = "unknown_block";
208 m_nodename = nodename;
210 // Legacy constructor
211 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
212 InventoryItem(gamedef, count)
214 INodeDefManager *ndef = m_gamedef->ndef();
215 std::string nodename = ndef->get(content).name;
217 nodename = "unknown_block";
218 m_nodename = nodename;
222 video::ITexture * MaterialItem::getImage() const
224 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
228 bool MaterialItem::isCookable() const
230 INodeDefManager *ndef = m_gamedef->ndef();
231 const ContentFeatures &f = ndef->get(m_nodename);
232 return (f.cookresult_item != "");
235 InventoryItem *MaterialItem::createCookResult() const
237 INodeDefManager *ndef = m_gamedef->ndef();
238 const ContentFeatures &f = ndef->get(m_nodename);
239 std::istringstream is(f.cookresult_item, std::ios::binary);
240 return InventoryItem::deSerialize(is, m_gamedef);
243 float MaterialItem::getCookTime() const
245 INodeDefManager *ndef = m_gamedef->ndef();
246 const ContentFeatures &f = ndef->get(m_nodename);
247 return f.furnace_cooktime;
250 float MaterialItem::getBurnTime() const
252 INodeDefManager *ndef = m_gamedef->ndef();
253 const ContentFeatures &f = ndef->get(m_nodename);
254 return f.furnace_burntime;
257 content_t MaterialItem::getMaterial() const
259 INodeDefManager *ndef = m_gamedef->ndef();
260 content_t id = CONTENT_IGNORE;
261 ndef->getId(m_nodename, id);
269 std::string ToolItem::getImageBasename() const
271 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
275 video::ITexture * ToolItem::getImage() const
277 ITextureSource *tsrc = m_gamedef->tsrc();
279 std::string basename = getImageBasename();
282 Calculate a progress value with sane amount of
285 u32 maxprogress = 30;
286 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
288 float value_f = (float)toolprogress / (float)maxprogress;
289 std::ostringstream os;
290 os<<basename<<"^[progressbar"<<value_f;
292 return tsrc->getTextureRaw(os.str());
295 video::ITexture * ToolItem::getImageRaw() const
297 ITextureSource *tsrc = m_gamedef->tsrc();
299 return tsrc->getTextureRaw(getImageBasename());
308 video::ITexture * CraftItem::getImage() const
310 ITextureSource *tsrc = m_gamedef->tsrc();
312 std::string name = item_craft_get_image_name(m_subname, m_gamedef);
314 // Get such a texture
315 return tsrc->getTextureRaw(name);
319 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, v3f pos)
322 ServerActiveObject *obj = item_craft_create_object(m_subname, env, pos);
326 return InventoryItem::createSAO(env, pos);
329 u16 CraftItem::getDropCount() const
332 s16 dc = item_craft_get_drop_count(m_subname, m_gamedef);
336 return InventoryItem::getDropCount();
339 bool CraftItem::isCookable() const
341 return item_craft_is_cookable(m_subname, m_gamedef);
344 InventoryItem *CraftItem::createCookResult() const
346 return item_craft_create_cook_result(m_subname, m_gamedef);
349 float CraftItem::getCookTime() const
354 float CraftItem::getBurnTime() const
356 if(m_subname == "lump_of_coal")
361 bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
363 if(!item_craft_is_eatable(m_subname, m_gamedef))
366 u16 result_count = getCount() - 1; // Eat one at a time
367 s16 hp_change = item_craft_eat_hp_change(m_subname, m_gamedef);
368 s16 hp = user->getHP();
377 setCount(result_count);
385 InventoryList::InventoryList(std::string name, u32 size)
393 InventoryList::~InventoryList()
395 for(u32 i=0; i<m_items.size(); i++)
402 void InventoryList::clearItems()
404 for(u32 i=0; i<m_items.size(); i++)
412 for(u32 i=0; i<m_size; i++)
414 m_items.push_back(NULL);
420 void InventoryList::serialize(std::ostream &os) const
422 //os.imbue(std::locale("C"));
424 for(u32 i=0; i<m_items.size(); i++)
426 InventoryItem *item = m_items[i];
439 os<<"EndInventoryList\n";
442 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
444 //is.imbue(std::locale("C"));
452 std::getline(is, line, '\n');
454 std::istringstream iss(line);
455 //iss.imbue(std::locale("C"));
458 std::getline(iss, name, ' ');
460 if(name == "EndInventoryList")
464 // This is a temporary backwards compatibility fix
465 else if(name == "end")
469 else if(name == "Item")
471 if(item_i > getSize() - 1)
472 throw SerializationError("too many items");
473 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
474 m_items[item_i++] = item;
476 else if(name == "Empty")
478 if(item_i > getSize() - 1)
479 throw SerializationError("too many items");
480 m_items[item_i++] = NULL;
484 throw SerializationError("Unknown inventory identifier");
489 InventoryList::InventoryList(const InventoryList &other)
492 Do this so that the items get cloned. Otherwise the pointers
493 in the array will just get copied.
498 InventoryList & InventoryList::operator = (const InventoryList &other)
500 m_name = other.m_name;
501 m_size = other.m_size;
503 for(u32 i=0; i<other.m_items.size(); i++)
505 InventoryItem *item = other.m_items[i];
508 m_items[i] = item->clone();
516 const std::string &InventoryList::getName() const
521 u32 InventoryList::getSize()
523 return m_items.size();
526 u32 InventoryList::getUsedSlots()
529 for(u32 i=0; i<m_items.size(); i++)
531 InventoryItem *item = m_items[i];
538 u32 InventoryList::getFreeSlots()
540 return getSize() - getUsedSlots();
543 const InventoryItem * InventoryList::getItem(u32 i) const
545 if(i >= m_items.size())
550 InventoryItem * InventoryList::getItem(u32 i)
552 if(i >= m_items.size())
557 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
559 if(i >= m_items.size())
562 InventoryItem *olditem = m_items[i];
563 m_items[i] = newitem;
568 void InventoryList::deleteItem(u32 i)
570 assert(i < m_items.size());
571 InventoryItem *item = changeItem(i, NULL);
576 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
582 First try to find if it could be added to some existing items
584 for(u32 i=0; i<m_items.size(); i++)
586 // Ignore empty slots
587 if(m_items[i] == NULL)
590 newitem = addItem(i, newitem);
592 return NULL; // All was eaten
596 Then try to add it to empty slots
598 for(u32 i=0; i<m_items.size(); i++)
600 // Ignore unempty slots
601 if(m_items[i] != NULL)
604 newitem = addItem(i, newitem);
606 return NULL; // All was eaten
613 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
617 if(i >= m_items.size())
622 // If it is an empty position, it's an easy job.
623 InventoryItem *to_item = getItem(i);
626 m_items[i] = newitem;
630 // If not addable, return the item
631 if(newitem->addableTo(to_item) == false)
634 // If the item fits fully in the slot, add counter and delete it
635 if(newitem->getCount() <= to_item->freeSpace())
637 to_item->add(newitem->getCount());
641 // Else the item does not fit fully. Add all that fits and return
645 u16 freespace = to_item->freeSpace();
646 to_item->add(freespace);
647 newitem->remove(freespace);
652 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
654 // If it is an empty position, it's an easy job.
655 const InventoryItem *to_item = getItem(i);
661 // If not addable, fail
662 if(newitem->addableTo(to_item) == false)
665 // If the item fits fully in the slot, pass
666 if(newitem->getCount() <= to_item->freeSpace())
674 bool InventoryList::roomForItem(const InventoryItem *item)
676 for(u32 i=0; i<m_items.size(); i++)
677 if(itemFits(i, item))
682 bool InventoryList::roomForCookedItem(const InventoryItem *item)
686 const InventoryItem *cook = item->createCookResult();
689 bool room = roomForItem(cook);
694 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
701 InventoryItem *item = getItem(i);
702 // If it is an empty position, return NULL
706 if(count >= item->getCount())
708 // Get the item by swapping NULL to its place
709 return changeItem(i, NULL);
713 InventoryItem *item2 = item->clone();
715 item2->setCount(count);
722 void InventoryList::decrementMaterials(u16 count)
724 for(u32 i=0; i<m_items.size(); i++)
726 InventoryItem *item = takeItem(i, count);
732 void InventoryList::print(std::ostream &o)
734 o<<"InventoryList:"<<std::endl;
735 for(u32 i=0; i<m_items.size(); i++)
737 InventoryItem *item = m_items[i];
751 Inventory::~Inventory()
756 void Inventory::clear()
758 for(u32 i=0; i<m_lists.size(); i++)
765 Inventory::Inventory()
769 Inventory::Inventory(const Inventory &other)
774 Inventory & Inventory::operator = (const Inventory &other)
777 for(u32 i=0; i<other.m_lists.size(); i++)
779 m_lists.push_back(new InventoryList(*other.m_lists[i]));
784 void Inventory::serialize(std::ostream &os) const
786 for(u32 i=0; i<m_lists.size(); i++)
788 InventoryList *list = m_lists[i];
789 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
793 os<<"EndInventory\n";
796 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
803 std::getline(is, line, '\n');
805 std::istringstream iss(line);
808 std::getline(iss, name, ' ');
810 if(name == "EndInventory")
814 // This is a temporary backwards compatibility fix
815 else if(name == "end")
819 else if(name == "List")
821 std::string listname;
824 std::getline(iss, listname, ' ');
827 InventoryList *list = new InventoryList(listname, listsize);
828 list->deSerialize(is, gamedef);
830 m_lists.push_back(list);
834 throw SerializationError("Unknown inventory identifier");
839 InventoryList * Inventory::addList(const std::string &name, u32 size)
841 s32 i = getListIndex(name);
844 if(m_lists[i]->getSize() != size)
847 m_lists[i] = new InventoryList(name, size);
853 m_lists.push_back(new InventoryList(name, size));
854 return m_lists.getLast();
858 InventoryList * Inventory::getList(const std::string &name)
860 s32 i = getListIndex(name);
866 bool Inventory::deleteList(const std::string &name)
868 s32 i = getListIndex(name);
876 const InventoryList * Inventory::getList(const std::string &name) const
878 s32 i = getListIndex(name);
884 const s32 Inventory::getListIndex(const std::string &name) const
886 for(u32 i=0; i<m_lists.size(); i++)
888 if(m_lists[i]->getName() == name)
898 InventoryAction * InventoryAction::deSerialize(std::istream &is)
901 std::getline(is, type, ' ');
903 InventoryAction *a = NULL;
907 a = new IMoveAction(is);
909 else if(type == "Drop")
911 a = new IDropAction(is);
917 static std::string describeC(const struct InventoryContext *c)
919 if(c->current_player == NULL)
920 return "current_player=NULL";
922 return std::string("current_player=") + c->current_player->getName();
925 IMoveAction::IMoveAction(std::istream &is)
929 std::getline(is, ts, ' ');
932 std::getline(is, from_inv, ' ');
934 std::getline(is, from_list, ' ');
936 std::getline(is, ts, ' ');
939 std::getline(is, to_inv, ' ');
941 std::getline(is, to_list, ' ');
943 std::getline(is, ts, ' ');
947 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
948 ServerEnvironment *env)
950 Inventory *inv_from = mgr->getInventory(c, from_inv);
951 Inventory *inv_to = mgr->getInventory(c, to_inv);
954 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
955 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
956 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
960 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
961 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
962 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
966 InventoryList *list_from = inv_from->getList(from_list);
967 InventoryList *list_to = inv_to->getList(to_list);
970 If a list doesn't exist or the source item doesn't exist
973 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
974 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
975 <<", from_list=\""<<from_list<<"\""<<std::endl;
979 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
980 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
981 <<", to_list=\""<<to_list<<"\""<<std::endl;
984 if(list_from->getItem(from_i) == NULL)
986 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
987 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
988 <<", from_list=\""<<from_list<<"\""
989 <<" from_i="<<from_i<<std::endl;
993 If the source and the destination slots are the same
995 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
997 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
998 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
999 <<"\" i="<<from_i<<std::endl;
1003 // Take item from source list
1004 InventoryItem *item1 = NULL;
1006 item1 = list_from->changeItem(from_i, NULL);
1008 item1 = list_from->takeItem(from_i, count);
1010 // Try to add the item to destination list
1011 InventoryItem *olditem = item1;
1012 item1 = list_to->addItem(to_i, item1);
1014 // If something is returned, the item was not fully added
1017 // If olditem is returned, nothing was added.
1018 bool nothing_added = (item1 == olditem);
1020 // If something else is returned, part of the item was left unadded.
1021 // Add the other part back to the source item
1022 list_from->addItem(from_i, item1);
1024 // If olditem is returned, nothing was added.
1028 // Take item from source list
1029 item1 = list_from->changeItem(from_i, NULL);
1030 // Adding was not possible, swap the items.
1031 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1032 // Put item from destination list to the source list
1033 list_from->changeItem(from_i, item2);
1037 mgr->inventoryModified(c, from_inv);
1038 if(from_inv != to_inv)
1039 mgr->inventoryModified(c, to_inv);
1041 infostream<<"IMoveAction::apply(): moved at "
1042 <<"["<<describeC(c)<<"]"
1043 <<" from inv=\""<<from_inv<<"\""
1044 <<" list=\""<<from_list<<"\""
1046 <<" to inv=\""<<to_inv<<"\""
1047 <<" list=\""<<to_list<<"\""
1052 IDropAction::IDropAction(std::istream &is)
1056 std::getline(is, ts, ' ');
1059 std::getline(is, from_inv, ' ');
1061 std::getline(is, from_list, ' ');
1063 std::getline(is, ts, ' ');
1067 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1068 ServerEnvironment *env)
1070 Inventory *inv_from = mgr->getInventory(c, from_inv);
1073 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1074 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1078 InventoryList *list_from = inv_from->getList(from_list);
1081 If a list doesn't exist or the source item doesn't exist
1084 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1085 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1086 <<", from_list=\""<<from_list<<"\""<<std::endl;
1089 if(list_from->getItem(from_i) == NULL)
1091 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1092 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1093 <<", from_list=\""<<from_list<<"\""
1094 <<" from_i="<<from_i<<std::endl;
1098 v3f pos = c->current_player->getPosition();
1100 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
1103 Ensure that the block is loaded so that the item
1104 can properly be added to the static list too
1106 MapBlock *block = env->getMap().emergeBlock(blockpos, false);
1109 infostream<<"IDropAction::apply(): FAIL: block not found: "
1110 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
1115 // Take item from source list
1117 count = list_from->getItem(from_i)->getDropCount();
1118 InventoryItem *item1 = list_from->takeItem(from_i, count);
1120 // Create an active object
1121 ServerActiveObject *obj = item1->createSAO(env, pos);
1124 infostream<<"IDropAction::apply(): item resulted in NULL object, "
1125 <<"not placing onto map"
1130 // Add the object to the environment
1131 env->addActiveObject(obj);
1133 infostream<<"Dropped object"<<std::endl;
1136 mgr->inventoryModified(c, from_inv);
1138 infostream<<"IDropAction::apply(): dropped "
1139 <<"["<<describeC(c)<<"]"
1140 <<" from inv=\""<<from_inv<<"\""
1141 <<" list=\""<<from_list<<"\""
1147 Craft checking system
1150 bool ItemSpec::checkItem(const InventoryItem *item) const
1152 if(type == ITEM_NONE)
1154 // Has to be no item
1160 // There should be an item
1164 std::string itemname = item->getName();
1166 if(type == ITEM_MATERIAL)
1168 if(itemname != "MaterialItem")
1170 MaterialItem *mitem = (MaterialItem*)item;
1172 if(mitem->getMaterial() != num)
1175 if(mitem->getNodeName() != name)
1179 else if(type == ITEM_CRAFT)
1181 if(itemname != "CraftItem")
1183 CraftItem *mitem = (CraftItem*)item;
1184 if(mitem->getSubName() != name)
1187 else if(type == ITEM_TOOL)
1189 // Not supported yet
1192 else if(type == ITEM_MBO)
1194 // Not supported yet
1199 // Not supported yet
1205 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1207 u16 items_min_x = 100;
1208 u16 items_max_x = 100;
1209 u16 items_min_y = 100;
1210 u16 items_max_y = 100;
1211 for(u16 y=0; y<3; y++)
1212 for(u16 x=0; x<3; x++)
1214 if(items[y*3 + x] == NULL)
1216 if(items_min_x == 100 || x < items_min_x)
1218 if(items_min_y == 100 || y < items_min_y)
1220 if(items_max_x == 100 || x > items_max_x)
1222 if(items_max_y == 100 || y > items_max_y)
1225 // No items at all, just return false
1226 if(items_min_x == 100)
1229 u16 items_w = items_max_x - items_min_x + 1;
1230 u16 items_h = items_max_y - items_min_y + 1;
1232 u16 specs_min_x = 100;
1233 u16 specs_max_x = 100;
1234 u16 specs_min_y = 100;
1235 u16 specs_max_y = 100;
1236 for(u16 y=0; y<3; y++)
1237 for(u16 x=0; x<3; x++)
1239 if(specs[y*3 + x].type == ITEM_NONE)
1241 if(specs_min_x == 100 || x < specs_min_x)
1243 if(specs_min_y == 100 || y < specs_min_y)
1245 if(specs_max_x == 100 || x > specs_max_x)
1247 if(specs_max_y == 100 || y > specs_max_y)
1250 // No specs at all, just return false
1251 if(specs_min_x == 100)
1254 u16 specs_w = specs_max_x - specs_min_x + 1;
1255 u16 specs_h = specs_max_y - specs_min_y + 1;
1258 if(items_w != specs_w || items_h != specs_h)
1261 for(u16 y=0; y<specs_h; y++)
1262 for(u16 x=0; x<specs_w; x++)
1264 u16 items_x = items_min_x + x;
1265 u16 items_y = items_min_y + y;
1266 u16 specs_x = specs_min_x + x;
1267 u16 specs_y = specs_min_y + y;
1268 const InventoryItem *item = items[items_y * 3 + items_x];
1269 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1271 if(spec.checkItem(item) == false)
1278 bool checkItemCombination(const InventoryItem * const * items,
1279 const InventoryItem * const * specs)
1281 u16 items_min_x = 100;
1282 u16 items_max_x = 100;
1283 u16 items_min_y = 100;
1284 u16 items_max_y = 100;
1285 for(u16 y=0; y<3; y++)
1286 for(u16 x=0; x<3; x++)
1288 if(items[y*3 + x] == NULL)
1290 if(items_min_x == 100 || x < items_min_x)
1292 if(items_min_y == 100 || y < items_min_y)
1294 if(items_max_x == 100 || x > items_max_x)
1296 if(items_max_y == 100 || y > items_max_y)
1299 // No items at all, just return false
1300 if(items_min_x == 100)
1303 u16 items_w = items_max_x - items_min_x + 1;
1304 u16 items_h = items_max_y - items_min_y + 1;
1306 u16 specs_min_x = 100;
1307 u16 specs_max_x = 100;
1308 u16 specs_min_y = 100;
1309 u16 specs_max_y = 100;
1310 for(u16 y=0; y<3; y++)
1311 for(u16 x=0; x<3; x++)
1313 if(specs[y*3 + x] == NULL)
1315 if(specs_min_x == 100 || x < specs_min_x)
1317 if(specs_min_y == 100 || y < specs_min_y)
1319 if(specs_max_x == 100 || x > specs_max_x)
1321 if(specs_max_y == 100 || y > specs_max_y)
1324 // No specs at all, just return false
1325 if(specs_min_x == 100)
1328 u16 specs_w = specs_max_x - specs_min_x + 1;
1329 u16 specs_h = specs_max_y - specs_min_y + 1;
1332 if(items_w != specs_w || items_h != specs_h)
1335 for(u16 y=0; y<specs_h; y++)
1336 for(u16 x=0; x<specs_w; x++)
1338 u16 items_x = items_min_x + x;
1339 u16 items_y = items_min_y + y;
1340 u16 specs_x = specs_min_x + x;
1341 u16 specs_y = specs_min_y + y;
1342 const InventoryItem *item = items[items_y * 3 + items_x];
1343 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1345 if(item == NULL && spec == NULL)
1347 if(item == NULL && spec != NULL)
1349 if(item != NULL && spec == NULL)
1351 if(!spec->isSubsetOf(item))