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"
36 #include "craftitemdef.h"
38 #include "scriptapi.h"
45 InventoryItem::InventoryItem(IGameDef *gamedef, u16 count):
52 InventoryItem::~InventoryItem()
56 content_t content_translate_from_19_to_internal(content_t c_from)
58 for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
60 if(trans_table_19[i][1] == c_from)
62 return trans_table_19[i][0];
68 InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef)
70 DSTACK(__FUNCTION_NAME);
72 //is.imbue(std::locale("C"));
75 std::getline(is, name, ' ');
77 if(name == "MaterialItem")
79 // u16 reads directly as a number (u8 doesn't)
84 // Convert old materials
86 material = content_translate_from_19_to_internal(material);
87 if(material > MAX_CONTENT)
88 throw SerializationError("Too large material number");
89 return new MaterialItem(gamedef, material, count);
91 else if(name == "MaterialItem2")
97 if(material > MAX_CONTENT)
98 throw SerializationError("Too large material number");
99 return new MaterialItem(gamedef, material, count);
101 else if(name == "NodeItem" || name == "MaterialItem3")
104 std::getline(is, all, '\n');
105 std::string nodename;
106 // First attempt to read inside ""
109 // If didn't skip to end, we have ""s
111 nodename = fnd.next("\"");
112 } else { // No luck, just read a word then
114 nodename = fnd.next(" ");
117 u16 count = stoi(trim(fnd.next("")));
118 return new MaterialItem(gamedef, nodename, count);
120 else if(name == "MBOItem")
122 std::string inventorystring;
123 std::getline(is, inventorystring, '|');
124 throw SerializationError("MBOItem not supported anymore");
126 else if(name == "CraftItem")
129 std::getline(is, all, '\n');
131 // First attempt to read inside ""
134 // If didn't skip to end, we have ""s
136 subname = fnd.next("\"");
137 } else { // No luck, just read a word then
139 subname = fnd.next(" ");
143 u16 count = stoi(trim(fnd.next("")));
144 return new CraftItem(gamedef, subname, count);
146 else if(name == "ToolItem")
149 std::getline(is, all, '\n');
150 std::string toolname;
151 // First attempt to read inside ""
154 // If didn't skip to end, we have ""s
156 toolname = fnd.next("\"");
157 } else { // No luck, just read a word then
159 toolname = fnd.next(" ");
163 u16 wear = stoi(trim(fnd.next("")));
164 return new ToolItem(gamedef, toolname, wear);
168 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
169 throw SerializationError("Unknown InventoryItem name");
173 InventoryItem* InventoryItem::deSerialize(const std::string &str,
176 std::istringstream is(str, std::ios_base::binary);
177 return deSerialize(is, gamedef);
180 std::string InventoryItem::getItemString() {
182 std::ostringstream os(std::ios_base::binary);
187 bool InventoryItem::dropOrPlace(ServerEnvironment *env,
188 ServerActiveObject *dropper,
189 v3f pos, bool place, s16 count)
192 Ensure that the block is loaded so that the item
193 can properly be added to the static list too
195 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
196 MapBlock *block = env->getMap().emergeBlock(blockpos, false);
199 infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: "
200 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
206 Take specified number of items,
207 but limit to getDropCount().
209 s16 dropcount = getDropCount();
210 if(count < 0 || count > dropcount)
212 if(count < 0 || count > getCount());
219 pos.Y -= BS*0.25; // let it drop a bit
221 //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
222 //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
224 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
225 // Add the object to the environment
226 env->addActiveObject(obj);
227 infostream<<"Dropped item"<<std::endl;
229 setCount(getCount() - count);
232 return getCount() < 1; // delete the item?
239 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
240 InventoryItem(gamedef, count)
243 nodename = "unknown_block";
244 m_nodename = nodename;
246 // Legacy constructor
247 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
248 InventoryItem(gamedef, count)
250 INodeDefManager *ndef = m_gamedef->ndef();
251 std::string nodename = ndef->get(content).name;
253 nodename = "unknown_block";
254 m_nodename = nodename;
258 video::ITexture * MaterialItem::getImage() const
260 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
264 bool MaterialItem::isCookable() const
266 INodeDefManager *ndef = m_gamedef->ndef();
267 const ContentFeatures &f = ndef->get(m_nodename);
268 return (f.cookresult_item != "");
271 InventoryItem *MaterialItem::createCookResult() const
273 INodeDefManager *ndef = m_gamedef->ndef();
274 const ContentFeatures &f = ndef->get(m_nodename);
275 std::istringstream is(f.cookresult_item, std::ios::binary);
276 return InventoryItem::deSerialize(is, m_gamedef);
279 float MaterialItem::getCookTime() const
281 INodeDefManager *ndef = m_gamedef->ndef();
282 const ContentFeatures &f = ndef->get(m_nodename);
283 return f.furnace_cooktime;
286 float MaterialItem::getBurnTime() const
288 INodeDefManager *ndef = m_gamedef->ndef();
289 const ContentFeatures &f = ndef->get(m_nodename);
290 return f.furnace_burntime;
293 content_t MaterialItem::getMaterial() const
295 INodeDefManager *ndef = m_gamedef->ndef();
296 content_t id = CONTENT_IGNORE;
297 ndef->getId(m_nodename, id);
305 std::string ToolItem::getImageBasename() const
307 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
311 video::ITexture * ToolItem::getImage() const
313 ITextureSource *tsrc = m_gamedef->tsrc();
315 std::string basename = getImageBasename();
318 Calculate a progress value with sane amount of
321 u32 maxprogress = 30;
322 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
324 float value_f = (float)toolprogress / (float)maxprogress;
325 std::ostringstream os;
326 os<<basename<<"^[progressbar"<<value_f;
328 return tsrc->getTextureRaw(os.str());
331 video::ITexture * ToolItem::getImageRaw() const
333 ITextureSource *tsrc = m_gamedef->tsrc();
335 return tsrc->getTextureRaw(getImageBasename());
344 video::ITexture * CraftItem::getImage() const
346 ICraftItemDefManager *cidef = m_gamedef->cidef();
347 ITextureSource *tsrc = m_gamedef->tsrc();
348 std::string imagename = cidef->getImagename(m_subname);
349 return tsrc->getTextureRaw(imagename);
353 u16 CraftItem::getStackMax() const
355 ICraftItemDefManager *cidef = m_gamedef->cidef();
356 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
358 return InventoryItem::getStackMax();
359 return def->stack_max;
362 bool CraftItem::isUsable() const
364 ICraftItemDefManager *cidef = m_gamedef->cidef();
365 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
366 return def != NULL && def->usable;
369 bool CraftItem::isCookable() const
371 ICraftItemDefManager *cidef = m_gamedef->cidef();
372 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
373 return def != NULL && def->cookresult_item != "";
376 InventoryItem *CraftItem::createCookResult() const
378 ICraftItemDefManager *cidef = m_gamedef->cidef();
379 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
381 return InventoryItem::createCookResult();
382 std::istringstream is(def->cookresult_item, std::ios::binary);
383 return InventoryItem::deSerialize(is, m_gamedef);
386 float CraftItem::getCookTime() const
388 ICraftItemDefManager *cidef = m_gamedef->cidef();
389 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
391 return InventoryItem::getCookTime();
392 return def->furnace_cooktime;
395 float CraftItem::getBurnTime() const
397 ICraftItemDefManager *cidef = m_gamedef->cidef();
398 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
400 return InventoryItem::getBurnTime();
401 return def->furnace_burntime;
404 s16 CraftItem::getDropCount() const
407 ICraftItemDefManager *cidef = m_gamedef->cidef();
408 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
409 if(def != NULL && def->dropcount >= 0)
410 return def->dropcount;
412 return InventoryItem::getDropCount();
415 bool CraftItem::areLiquidsPointable() const
417 ICraftItemDefManager *cidef = m_gamedef->cidef();
418 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
419 return def != NULL && def->liquids_pointable;
422 bool CraftItem::dropOrPlace(ServerEnvironment *env,
423 ServerActiveObject *dropper,
424 v3f pos, bool place, s16 count)
429 bool callback_exists = false;
434 result = scriptapi_craftitem_on_place_on_ground(
436 m_subname.c_str(), dropper, pos,
440 // note: on_drop is fallback for on_place_on_ground
444 result = scriptapi_craftitem_on_drop(
446 m_subname.c_str(), dropper, pos,
452 // If the callback returned true, drop one item
454 setCount(getCount() - 1);
455 return getCount() < 1;
459 // If neither on_place_on_ground (if place==true)
460 // nor on_drop exists, call the base implementation
461 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
465 bool CraftItem::use(ServerEnvironment *env,
466 ServerActiveObject *user,
467 const PointedThing& pointed)
469 bool callback_exists = false;
472 result = scriptapi_craftitem_on_use(
474 m_subname.c_str(), user, pointed,
479 // If the callback returned true, drop one item
481 setCount(getCount() - 1);
482 return getCount() < 1;
486 // If neither on_place_on_ground (if place==true)
487 // nor on_drop exists, call the base implementation
488 return InventoryItem::use(env, user, pointed);
496 InventoryList::InventoryList(std::string name, u32 size)
504 InventoryList::~InventoryList()
506 for(u32 i=0; i<m_items.size(); i++)
513 void InventoryList::clearItems()
515 for(u32 i=0; i<m_items.size(); i++)
523 for(u32 i=0; i<m_size; i++)
525 m_items.push_back(NULL);
531 void InventoryList::serialize(std::ostream &os) const
533 //os.imbue(std::locale("C"));
535 for(u32 i=0; i<m_items.size(); i++)
537 InventoryItem *item = m_items[i];
550 os<<"EndInventoryList\n";
553 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
555 //is.imbue(std::locale("C"));
563 std::getline(is, line, '\n');
565 std::istringstream iss(line);
566 //iss.imbue(std::locale("C"));
569 std::getline(iss, name, ' ');
571 if(name == "EndInventoryList")
575 // This is a temporary backwards compatibility fix
576 else if(name == "end")
580 else if(name == "Item")
582 if(item_i > getSize() - 1)
583 throw SerializationError("too many items");
584 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
585 m_items[item_i++] = item;
587 else if(name == "Empty")
589 if(item_i > getSize() - 1)
590 throw SerializationError("too many items");
591 m_items[item_i++] = NULL;
595 throw SerializationError("Unknown inventory identifier");
600 InventoryList::InventoryList(const InventoryList &other)
603 Do this so that the items get cloned. Otherwise the pointers
604 in the array will just get copied.
609 InventoryList & InventoryList::operator = (const InventoryList &other)
611 m_name = other.m_name;
612 m_size = other.m_size;
614 for(u32 i=0; i<other.m_items.size(); i++)
616 InventoryItem *item = other.m_items[i];
619 m_items[i] = item->clone();
627 const std::string &InventoryList::getName() const
632 u32 InventoryList::getSize()
634 return m_items.size();
637 u32 InventoryList::getUsedSlots()
640 for(u32 i=0; i<m_items.size(); i++)
642 InventoryItem *item = m_items[i];
649 u32 InventoryList::getFreeSlots()
651 return getSize() - getUsedSlots();
654 const InventoryItem * InventoryList::getItem(u32 i) const
656 if(i >= m_items.size())
661 InventoryItem * InventoryList::getItem(u32 i)
663 if(i >= m_items.size())
668 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
670 if(i >= m_items.size())
673 InventoryItem *olditem = m_items[i];
674 m_items[i] = newitem;
679 void InventoryList::deleteItem(u32 i)
681 assert(i < m_items.size());
682 InventoryItem *item = changeItem(i, NULL);
687 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
693 First try to find if it could be added to some existing items
695 for(u32 i=0; i<m_items.size(); i++)
697 // Ignore empty slots
698 if(m_items[i] == NULL)
701 newitem = addItem(i, newitem);
703 return NULL; // All was eaten
707 Then try to add it to empty slots
709 for(u32 i=0; i<m_items.size(); i++)
711 // Ignore unempty slots
712 if(m_items[i] != NULL)
715 newitem = addItem(i, newitem);
717 return NULL; // All was eaten
724 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
728 if(i >= m_items.size())
733 // If it is an empty position, it's an easy job.
734 InventoryItem *to_item = getItem(i);
737 m_items[i] = newitem;
741 // If not addable, return the item
742 if(newitem->addableTo(to_item) == false)
745 // If the item fits fully in the slot, add counter and delete it
746 if(newitem->getCount() <= to_item->freeSpace())
748 to_item->add(newitem->getCount());
752 // Else the item does not fit fully. Add all that fits and return
756 u16 freespace = to_item->freeSpace();
757 to_item->add(freespace);
758 newitem->remove(freespace);
763 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
765 // If it is an empty position, it's an easy job.
766 const InventoryItem *to_item = getItem(i);
772 // If not addable, fail
773 if(newitem->addableTo(to_item) == false)
776 // If the item fits fully in the slot, pass
777 if(newitem->getCount() <= to_item->freeSpace())
785 bool InventoryList::roomForItem(const InventoryItem *item)
787 for(u32 i=0; i<m_items.size(); i++)
788 if(itemFits(i, item))
793 bool InventoryList::roomForCookedItem(const InventoryItem *item)
797 const InventoryItem *cook = item->createCookResult();
800 bool room = roomForItem(cook);
805 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
812 InventoryItem *item = getItem(i);
813 // If it is an empty position, return NULL
817 if(count >= item->getCount())
819 // Get the item by swapping NULL to its place
820 return changeItem(i, NULL);
824 InventoryItem *item2 = item->clone();
826 item2->setCount(count);
833 void InventoryList::decrementMaterials(u16 count)
835 for(u32 i=0; i<m_items.size(); i++)
837 InventoryItem *item = takeItem(i, count);
843 void InventoryList::print(std::ostream &o)
845 o<<"InventoryList:"<<std::endl;
846 for(u32 i=0; i<m_items.size(); i++)
848 InventoryItem *item = m_items[i];
862 Inventory::~Inventory()
867 void Inventory::clear()
869 for(u32 i=0; i<m_lists.size(); i++)
876 Inventory::Inventory()
880 Inventory::Inventory(const Inventory &other)
885 Inventory & Inventory::operator = (const Inventory &other)
888 for(u32 i=0; i<other.m_lists.size(); i++)
890 m_lists.push_back(new InventoryList(*other.m_lists[i]));
895 void Inventory::serialize(std::ostream &os) const
897 for(u32 i=0; i<m_lists.size(); i++)
899 InventoryList *list = m_lists[i];
900 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
904 os<<"EndInventory\n";
907 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
914 std::getline(is, line, '\n');
916 std::istringstream iss(line);
919 std::getline(iss, name, ' ');
921 if(name == "EndInventory")
925 // This is a temporary backwards compatibility fix
926 else if(name == "end")
930 else if(name == "List")
932 std::string listname;
935 std::getline(iss, listname, ' ');
938 InventoryList *list = new InventoryList(listname, listsize);
939 list->deSerialize(is, gamedef);
941 m_lists.push_back(list);
945 throw SerializationError("Unknown inventory identifier");
950 InventoryList * Inventory::addList(const std::string &name, u32 size)
952 s32 i = getListIndex(name);
955 if(m_lists[i]->getSize() != size)
958 m_lists[i] = new InventoryList(name, size);
964 m_lists.push_back(new InventoryList(name, size));
965 return m_lists.getLast();
969 InventoryList * Inventory::getList(const std::string &name)
971 s32 i = getListIndex(name);
977 bool Inventory::deleteList(const std::string &name)
979 s32 i = getListIndex(name);
987 const InventoryList * Inventory::getList(const std::string &name) const
989 s32 i = getListIndex(name);
995 const s32 Inventory::getListIndex(const std::string &name) const
997 for(u32 i=0; i<m_lists.size(); i++)
999 if(m_lists[i]->getName() == name)
1009 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1012 std::getline(is, type, ' ');
1014 InventoryAction *a = NULL;
1018 a = new IMoveAction(is);
1020 else if(type == "Drop")
1022 a = new IDropAction(is);
1028 static std::string describeC(const struct InventoryContext *c)
1030 if(c->current_player == NULL)
1031 return "current_player=NULL";
1033 return std::string("current_player=") + c->current_player->getName();
1036 IMoveAction::IMoveAction(std::istream &is)
1040 std::getline(is, ts, ' ');
1043 std::getline(is, from_inv, ' ');
1045 std::getline(is, from_list, ' ');
1047 std::getline(is, ts, ' ');
1050 std::getline(is, to_inv, ' ');
1052 std::getline(is, to_list, ' ');
1054 std::getline(is, ts, ' ');
1058 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1059 ServerEnvironment *env)
1061 Inventory *inv_from = mgr->getInventory(c, from_inv);
1062 Inventory *inv_to = mgr->getInventory(c, to_inv);
1065 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1066 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1067 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1071 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1072 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1073 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1077 InventoryList *list_from = inv_from->getList(from_list);
1078 InventoryList *list_to = inv_to->getList(to_list);
1081 If a list doesn't exist or the source item doesn't exist
1084 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1085 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1086 <<", from_list=\""<<from_list<<"\""<<std::endl;
1090 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1091 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1092 <<", to_list=\""<<to_list<<"\""<<std::endl;
1095 if(list_from->getItem(from_i) == NULL)
1097 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1098 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1099 <<", from_list=\""<<from_list<<"\""
1100 <<" from_i="<<from_i<<std::endl;
1104 If the source and the destination slots are the same
1106 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1108 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1109 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1110 <<"\" i="<<from_i<<std::endl;
1114 // Take item from source list
1115 InventoryItem *item1 = NULL;
1117 item1 = list_from->changeItem(from_i, NULL);
1119 item1 = list_from->takeItem(from_i, count);
1121 // Try to add the item to destination list
1122 InventoryItem *olditem = item1;
1123 item1 = list_to->addItem(to_i, item1);
1125 // If something is returned, the item was not fully added
1128 // If olditem is returned, nothing was added.
1129 bool nothing_added = (item1 == olditem);
1131 // If something else is returned, part of the item was left unadded.
1132 // Add the other part back to the source item
1133 list_from->addItem(from_i, item1);
1135 // If olditem is returned, nothing was added.
1139 // Take item from source list
1140 item1 = list_from->changeItem(from_i, NULL);
1141 // Adding was not possible, swap the items.
1142 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1143 // Put item from destination list to the source list
1144 list_from->changeItem(from_i, item2);
1148 mgr->inventoryModified(c, from_inv);
1149 if(from_inv != to_inv)
1150 mgr->inventoryModified(c, to_inv);
1152 infostream<<"IMoveAction::apply(): moved at "
1153 <<"["<<describeC(c)<<"]"
1154 <<" from inv=\""<<from_inv<<"\""
1155 <<" list=\""<<from_list<<"\""
1157 <<" to inv=\""<<to_inv<<"\""
1158 <<" list=\""<<to_list<<"\""
1163 IDropAction::IDropAction(std::istream &is)
1167 std::getline(is, ts, ' ');
1170 std::getline(is, from_inv, ' ');
1172 std::getline(is, from_list, ' ');
1174 std::getline(is, ts, ' ');
1178 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1179 ServerEnvironment *env)
1181 if(c->current_player == NULL){
1182 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1186 // Do NOT cast directly to ServerActiveObject*, it breaks
1187 // because of multiple inheritance.
1188 ServerActiveObject *dropper =
1189 static_cast<ServerActiveObject*>(
1190 static_cast<ServerRemotePlayer*>(
1194 Inventory *inv_from = mgr->getInventory(c, from_inv);
1197 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1198 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1202 InventoryList *list_from = inv_from->getList(from_list);
1205 If a list doesn't exist or the source item doesn't exist
1208 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1209 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1210 <<", from_list=\""<<from_list<<"\""<<std::endl;
1213 InventoryItem *item = list_from->getItem(from_i);
1216 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1217 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1218 <<", from_list=\""<<from_list<<"\""
1219 <<" from_i="<<from_i<<std::endl;
1223 v3f pos = dropper->getBasePosition();
1233 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1235 list_from->deleteItem(from_i);
1237 mgr->inventoryModified(c, from_inv);
1239 infostream<<"IDropAction::apply(): dropped "
1240 <<"["<<describeC(c)<<"]"
1241 <<" from inv=\""<<from_inv<<"\""
1242 <<" list=\""<<from_list<<"\""
1248 Craft checking system
1251 bool ItemSpec::checkItem(const InventoryItem *item) const
1253 if(type == ITEM_NONE)
1255 // Has to be no item
1261 // There should be an item
1265 std::string itemname = item->getName();
1267 if(type == ITEM_MATERIAL)
1269 if(itemname != "MaterialItem")
1271 MaterialItem *mitem = (MaterialItem*)item;
1273 if(mitem->getMaterial() != num)
1276 if(mitem->getNodeName() != name)
1280 else if(type == ITEM_CRAFT)
1282 if(itemname != "CraftItem")
1284 CraftItem *mitem = (CraftItem*)item;
1285 if(mitem->getSubName() != name)
1288 else if(type == ITEM_TOOL)
1290 // Not supported yet
1293 else if(type == ITEM_MBO)
1295 // Not supported yet
1300 // Not supported yet
1306 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1308 u16 items_min_x = 100;
1309 u16 items_max_x = 100;
1310 u16 items_min_y = 100;
1311 u16 items_max_y = 100;
1312 for(u16 y=0; y<3; y++)
1313 for(u16 x=0; x<3; x++)
1315 if(items[y*3 + x] == NULL)
1317 if(items_min_x == 100 || x < items_min_x)
1319 if(items_min_y == 100 || y < items_min_y)
1321 if(items_max_x == 100 || x > items_max_x)
1323 if(items_max_y == 100 || y > items_max_y)
1326 // No items at all, just return false
1327 if(items_min_x == 100)
1330 u16 items_w = items_max_x - items_min_x + 1;
1331 u16 items_h = items_max_y - items_min_y + 1;
1333 u16 specs_min_x = 100;
1334 u16 specs_max_x = 100;
1335 u16 specs_min_y = 100;
1336 u16 specs_max_y = 100;
1337 for(u16 y=0; y<3; y++)
1338 for(u16 x=0; x<3; x++)
1340 if(specs[y*3 + x].type == ITEM_NONE)
1342 if(specs_min_x == 100 || x < specs_min_x)
1344 if(specs_min_y == 100 || y < specs_min_y)
1346 if(specs_max_x == 100 || x > specs_max_x)
1348 if(specs_max_y == 100 || y > specs_max_y)
1351 // No specs at all, just return false
1352 if(specs_min_x == 100)
1355 u16 specs_w = specs_max_x - specs_min_x + 1;
1356 u16 specs_h = specs_max_y - specs_min_y + 1;
1359 if(items_w != specs_w || items_h != specs_h)
1362 for(u16 y=0; y<specs_h; y++)
1363 for(u16 x=0; x<specs_w; x++)
1365 u16 items_x = items_min_x + x;
1366 u16 items_y = items_min_y + y;
1367 u16 specs_x = specs_min_x + x;
1368 u16 specs_y = specs_min_y + y;
1369 const InventoryItem *item = items[items_y * 3 + items_x];
1370 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1372 if(spec.checkItem(item) == false)
1379 bool checkItemCombination(const InventoryItem * const * items,
1380 const InventoryItem * const * specs)
1382 u16 items_min_x = 100;
1383 u16 items_max_x = 100;
1384 u16 items_min_y = 100;
1385 u16 items_max_y = 100;
1386 for(u16 y=0; y<3; y++)
1387 for(u16 x=0; x<3; x++)
1389 if(items[y*3 + x] == NULL)
1391 if(items_min_x == 100 || x < items_min_x)
1393 if(items_min_y == 100 || y < items_min_y)
1395 if(items_max_x == 100 || x > items_max_x)
1397 if(items_max_y == 100 || y > items_max_y)
1400 // No items at all, just return false
1401 if(items_min_x == 100)
1404 u16 items_w = items_max_x - items_min_x + 1;
1405 u16 items_h = items_max_y - items_min_y + 1;
1407 u16 specs_min_x = 100;
1408 u16 specs_max_x = 100;
1409 u16 specs_min_y = 100;
1410 u16 specs_max_y = 100;
1411 for(u16 y=0; y<3; y++)
1412 for(u16 x=0; x<3; x++)
1414 if(specs[y*3 + x] == NULL)
1416 if(specs_min_x == 100 || x < specs_min_x)
1418 if(specs_min_y == 100 || y < specs_min_y)
1420 if(specs_max_x == 100 || x > specs_max_x)
1422 if(specs_max_y == 100 || y > specs_max_y)
1425 // No specs at all, just return false
1426 if(specs_min_x == 100)
1429 u16 specs_w = specs_max_x - specs_min_x + 1;
1430 u16 specs_h = specs_max_y - specs_min_y + 1;
1433 if(items_w != specs_w || items_h != specs_h)
1436 for(u16 y=0; y<specs_h; y++)
1437 for(u16 x=0; x<specs_w; x++)
1439 u16 items_x = items_min_x + x;
1440 u16 items_y = items_min_y + y;
1441 u16 specs_x = specs_min_x + x;
1442 u16 specs_y = specs_min_y + y;
1443 const InventoryItem *item = items[items_y * 3 + items_x];
1444 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1446 if(item == NULL && spec == NULL)
1448 if(item == NULL && spec != NULL)
1450 if(item != NULL && spec == NULL)
1452 if(!spec->isSubsetOf(item))