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_sao.h"
29 #include "environment.h"
35 #include "craftitemdef.h"
37 #include "scriptapi.h"
44 InventoryItem::InventoryItem(IGameDef *gamedef, u16 count):
51 InventoryItem::~InventoryItem()
55 content_t content_translate_from_19_to_internal(content_t c_from)
57 for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
59 if(trans_table_19[i][1] == c_from)
61 return trans_table_19[i][0];
67 InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef)
69 DSTACK(__FUNCTION_NAME);
71 //is.imbue(std::locale("C"));
74 std::getline(is, name, ' ');
76 if(name == "MaterialItem")
78 // u16 reads directly as a number (u8 doesn't)
83 // Convert old materials
85 material = content_translate_from_19_to_internal(material);
86 if(material > MAX_CONTENT)
87 throw SerializationError("Too large material number");
88 return new MaterialItem(gamedef, material, count);
90 else if(name == "MaterialItem2")
96 if(material > MAX_CONTENT)
97 throw SerializationError("Too large material number");
98 return new MaterialItem(gamedef, material, count);
100 else if(name == "NodeItem" || name == "MaterialItem3")
103 std::getline(is, all, '\n');
104 std::string nodename;
105 // First attempt to read inside ""
108 // If didn't skip to end, we have ""s
110 nodename = fnd.next("\"");
111 } else { // No luck, just read a word then
113 nodename = fnd.next(" ");
116 u16 count = stoi(trim(fnd.next("")));
119 return new MaterialItem(gamedef, nodename, count);
121 else if(name == "MBOItem")
123 std::string inventorystring;
124 std::getline(is, inventorystring, '|');
125 throw SerializationError("MBOItem not supported anymore");
127 else if(name == "CraftItem")
130 std::getline(is, all, '\n');
132 // First attempt to read inside ""
135 // If didn't skip to end, we have ""s
137 subname = fnd.next("\"");
138 } else { // No luck, just read a word then
140 subname = fnd.next(" ");
144 u16 count = stoi(trim(fnd.next("")));
147 return new CraftItem(gamedef, subname, count);
149 else if(name == "ToolItem")
152 std::getline(is, all, '\n');
153 std::string toolname;
154 // First attempt to read inside ""
157 // If didn't skip to end, we have ""s
159 toolname = fnd.next("\"");
160 } else { // No luck, just read a word then
162 toolname = fnd.next(" ");
166 u16 wear = stoi(trim(fnd.next("")));
167 return new ToolItem(gamedef, toolname, wear);
171 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
172 throw SerializationError("Unknown InventoryItem name");
176 InventoryItem* InventoryItem::deSerialize(const std::string &str,
179 std::istringstream is(str, std::ios_base::binary);
180 return deSerialize(is, gamedef);
183 std::string InventoryItem::getItemString() {
185 std::ostringstream os(std::ios_base::binary);
190 bool InventoryItem::dropOrPlace(ServerEnvironment *env,
191 ServerActiveObject *dropper,
192 v3f pos, bool place, s16 count)
195 Ensure that the block is loaded so that the item
196 can properly be added to the static list too
198 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
199 MapBlock *block = env->getMap().emergeBlock(blockpos, false);
202 infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: "
203 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
209 Take specified number of items,
210 but limit to getDropCount().
212 s16 dropcount = getDropCount();
213 if(count < 0 || count > dropcount)
215 if(count < 0 || count > getCount())
222 pos.Y -= BS*0.25; // let it drop a bit
224 //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
225 //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
227 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
228 // Add the object to the environment
229 env->addActiveObject(obj);
230 infostream<<"Dropped item"<<std::endl;
232 setCount(getCount() - count);
235 return getCount() < 1; // delete the item?
242 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
243 InventoryItem(gamedef, count)
246 nodename = "unknown_block";
247 m_nodename = nodename;
249 // Legacy constructor
250 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
251 InventoryItem(gamedef, count)
253 INodeDefManager *ndef = m_gamedef->ndef();
254 std::string nodename = ndef->get(content).name;
256 nodename = "unknown_block";
257 m_nodename = nodename;
261 video::ITexture * MaterialItem::getImage() const
263 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
267 bool MaterialItem::isCookable() const
269 INodeDefManager *ndef = m_gamedef->ndef();
270 const ContentFeatures &f = ndef->get(m_nodename);
271 return (f.cookresult_item != "");
274 InventoryItem *MaterialItem::createCookResult() const
276 INodeDefManager *ndef = m_gamedef->ndef();
277 const ContentFeatures &f = ndef->get(m_nodename);
278 std::istringstream is(f.cookresult_item, std::ios::binary);
279 return InventoryItem::deSerialize(is, m_gamedef);
282 float MaterialItem::getCookTime() const
284 INodeDefManager *ndef = m_gamedef->ndef();
285 const ContentFeatures &f = ndef->get(m_nodename);
286 return f.furnace_cooktime;
289 float MaterialItem::getBurnTime() const
291 INodeDefManager *ndef = m_gamedef->ndef();
292 const ContentFeatures &f = ndef->get(m_nodename);
293 return f.furnace_burntime;
296 content_t MaterialItem::getMaterial() const
298 INodeDefManager *ndef = m_gamedef->ndef();
299 content_t id = CONTENT_IGNORE;
300 ndef->getId(m_nodename, id);
308 std::string ToolItem::getImageBasename() const
310 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
314 video::ITexture * ToolItem::getImage() const
316 ITextureSource *tsrc = m_gamedef->tsrc();
318 std::string basename = getImageBasename();
321 Calculate a progress value with sane amount of
324 u32 maxprogress = 30;
325 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
327 float value_f = (float)toolprogress / (float)maxprogress;
328 std::ostringstream os;
329 os<<basename<<"^[progressbar"<<value_f;
331 return tsrc->getTextureRaw(os.str());
334 video::ITexture * ToolItem::getImageRaw() const
336 ITextureSource *tsrc = m_gamedef->tsrc();
338 return tsrc->getTextureRaw(getImageBasename());
347 video::ITexture * CraftItem::getImage() const
349 ICraftItemDefManager *cidef = m_gamedef->cidef();
350 ITextureSource *tsrc = m_gamedef->tsrc();
351 std::string imagename = cidef->getImagename(m_subname);
352 return tsrc->getTextureRaw(imagename);
356 u16 CraftItem::getStackMax() const
358 ICraftItemDefManager *cidef = m_gamedef->cidef();
359 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
361 return InventoryItem::getStackMax();
362 return def->stack_max;
365 bool CraftItem::isUsable() const
367 ICraftItemDefManager *cidef = m_gamedef->cidef();
368 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
369 return def != NULL && def->usable;
372 bool CraftItem::isCookable() const
374 ICraftItemDefManager *cidef = m_gamedef->cidef();
375 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
376 return def != NULL && def->cookresult_item != "";
379 InventoryItem *CraftItem::createCookResult() const
381 ICraftItemDefManager *cidef = m_gamedef->cidef();
382 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
384 return InventoryItem::createCookResult();
385 std::istringstream is(def->cookresult_item, std::ios::binary);
386 return InventoryItem::deSerialize(is, m_gamedef);
389 float CraftItem::getCookTime() const
391 ICraftItemDefManager *cidef = m_gamedef->cidef();
392 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
394 return InventoryItem::getCookTime();
395 return def->furnace_cooktime;
398 float CraftItem::getBurnTime() const
400 ICraftItemDefManager *cidef = m_gamedef->cidef();
401 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
403 return InventoryItem::getBurnTime();
404 return def->furnace_burntime;
407 s16 CraftItem::getDropCount() const
410 ICraftItemDefManager *cidef = m_gamedef->cidef();
411 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
412 if(def != NULL && def->dropcount >= 0)
413 return def->dropcount;
415 return InventoryItem::getDropCount();
418 bool CraftItem::areLiquidsPointable() const
420 ICraftItemDefManager *cidef = m_gamedef->cidef();
421 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
422 return def != NULL && def->liquids_pointable;
425 bool CraftItem::dropOrPlace(ServerEnvironment *env,
426 ServerActiveObject *dropper,
427 v3f pos, bool place, s16 count)
432 bool callback_exists = false;
437 result = scriptapi_craftitem_on_place_on_ground(
439 m_subname.c_str(), dropper, pos,
443 // note: on_drop is fallback for on_place_on_ground
447 result = scriptapi_craftitem_on_drop(
449 m_subname.c_str(), dropper, pos,
455 // If the callback returned true, drop one item
457 setCount(getCount() - 1);
458 return getCount() < 1;
462 // If neither on_place_on_ground (if place==true)
463 // nor on_drop exists, call the base implementation
464 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
468 bool CraftItem::use(ServerEnvironment *env,
469 ServerActiveObject *user,
470 const PointedThing& pointed)
472 bool callback_exists = false;
475 result = scriptapi_craftitem_on_use(
477 m_subname.c_str(), user, pointed,
482 // If the callback returned true, drop one item
484 setCount(getCount() - 1);
485 return getCount() < 1;
489 // If neither on_place_on_ground (if place==true)
490 // nor on_drop exists, call the base implementation
491 return InventoryItem::use(env, user, pointed);
499 InventoryList::InventoryList(std::string name, u32 size)
507 InventoryList::~InventoryList()
509 for(u32 i=0; i<m_items.size(); i++)
516 void InventoryList::clearItems()
518 for(u32 i=0; i<m_items.size(); i++)
526 for(u32 i=0; i<m_size; i++)
528 m_items.push_back(NULL);
534 void InventoryList::serialize(std::ostream &os) const
536 //os.imbue(std::locale("C"));
538 for(u32 i=0; i<m_items.size(); i++)
540 InventoryItem *item = m_items[i];
553 os<<"EndInventoryList\n";
556 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
558 //is.imbue(std::locale("C"));
566 std::getline(is, line, '\n');
568 std::istringstream iss(line);
569 //iss.imbue(std::locale("C"));
572 std::getline(iss, name, ' ');
574 if(name == "EndInventoryList")
578 // This is a temporary backwards compatibility fix
579 else if(name == "end")
583 else if(name == "Item")
585 if(item_i > getSize() - 1)
586 throw SerializationError("too many items");
587 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
588 m_items[item_i++] = item;
590 else if(name == "Empty")
592 if(item_i > getSize() - 1)
593 throw SerializationError("too many items");
594 m_items[item_i++] = NULL;
598 throw SerializationError("Unknown inventory identifier");
603 InventoryList::InventoryList(const InventoryList &other)
606 Do this so that the items get cloned. Otherwise the pointers
607 in the array will just get copied.
612 InventoryList & InventoryList::operator = (const InventoryList &other)
614 m_name = other.m_name;
615 m_size = other.m_size;
617 for(u32 i=0; i<other.m_items.size(); i++)
619 InventoryItem *item = other.m_items[i];
622 m_items[i] = item->clone();
630 const std::string &InventoryList::getName() const
635 u32 InventoryList::getSize()
637 return m_items.size();
640 u32 InventoryList::getUsedSlots()
643 for(u32 i=0; i<m_items.size(); i++)
645 InventoryItem *item = m_items[i];
652 u32 InventoryList::getFreeSlots()
654 return getSize() - getUsedSlots();
657 const InventoryItem * InventoryList::getItem(u32 i) const
659 if(i >= m_items.size())
664 InventoryItem * InventoryList::getItem(u32 i)
666 if(i >= m_items.size())
671 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
673 if(i >= m_items.size())
676 InventoryItem *olditem = m_items[i];
677 m_items[i] = newitem;
682 void InventoryList::deleteItem(u32 i)
684 assert(i < m_items.size());
685 InventoryItem *item = changeItem(i, NULL);
690 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
696 First try to find if it could be added to some existing items
698 for(u32 i=0; i<m_items.size(); i++)
700 // Ignore empty slots
701 if(m_items[i] == NULL)
704 newitem = addItem(i, newitem);
706 return NULL; // All was eaten
710 Then try to add it to empty slots
712 for(u32 i=0; i<m_items.size(); i++)
714 // Ignore unempty slots
715 if(m_items[i] != NULL)
718 newitem = addItem(i, newitem);
720 return NULL; // All was eaten
727 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
731 if(i >= m_items.size())
736 // If it is an empty position, it's an easy job.
737 InventoryItem *to_item = getItem(i);
740 m_items[i] = newitem;
744 // If not addable, return the item
745 if(newitem->addableTo(to_item) == false)
748 // If the item fits fully in the slot, add counter and delete it
749 if(newitem->getCount() <= to_item->freeSpace())
751 to_item->add(newitem->getCount());
755 // Else the item does not fit fully. Add all that fits and return
759 u16 freespace = to_item->freeSpace();
760 to_item->add(freespace);
761 newitem->remove(freespace);
766 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
768 // If it is an empty position, it's an easy job.
769 const InventoryItem *to_item = getItem(i);
775 // If not addable, fail
776 if(newitem->addableTo(to_item) == false)
779 // If the item fits fully in the slot, pass
780 if(newitem->getCount() <= to_item->freeSpace())
788 bool InventoryList::roomForItem(const InventoryItem *item)
790 for(u32 i=0; i<m_items.size(); i++)
791 if(itemFits(i, item))
796 bool InventoryList::roomForCookedItem(const InventoryItem *item)
800 const InventoryItem *cook = item->createCookResult();
803 bool room = roomForItem(cook);
808 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
815 InventoryItem *item = getItem(i);
816 // If it is an empty position, return NULL
820 if(count >= item->getCount())
822 // Get the item by swapping NULL to its place
823 return changeItem(i, NULL);
827 InventoryItem *item2 = item->clone();
829 item2->setCount(count);
836 void InventoryList::decrementMaterials(u16 count)
838 for(u32 i=0; i<m_items.size(); i++)
840 InventoryItem *item = takeItem(i, count);
846 void InventoryList::print(std::ostream &o)
848 o<<"InventoryList:"<<std::endl;
849 for(u32 i=0; i<m_items.size(); i++)
851 InventoryItem *item = m_items[i];
865 Inventory::~Inventory()
870 void Inventory::clear()
872 for(u32 i=0; i<m_lists.size(); i++)
879 Inventory::Inventory()
883 Inventory::Inventory(const Inventory &other)
888 Inventory & Inventory::operator = (const Inventory &other)
891 for(u32 i=0; i<other.m_lists.size(); i++)
893 m_lists.push_back(new InventoryList(*other.m_lists[i]));
898 void Inventory::serialize(std::ostream &os) const
900 for(u32 i=0; i<m_lists.size(); i++)
902 InventoryList *list = m_lists[i];
903 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
907 os<<"EndInventory\n";
910 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
917 std::getline(is, line, '\n');
919 std::istringstream iss(line);
922 std::getline(iss, name, ' ');
924 if(name == "EndInventory")
928 // This is a temporary backwards compatibility fix
929 else if(name == "end")
933 else if(name == "List")
935 std::string listname;
938 std::getline(iss, listname, ' ');
941 InventoryList *list = new InventoryList(listname, listsize);
942 list->deSerialize(is, gamedef);
944 m_lists.push_back(list);
948 throw SerializationError("Unknown inventory identifier");
953 InventoryList * Inventory::addList(const std::string &name, u32 size)
955 s32 i = getListIndex(name);
958 if(m_lists[i]->getSize() != size)
961 m_lists[i] = new InventoryList(name, size);
967 m_lists.push_back(new InventoryList(name, size));
968 return m_lists.getLast();
972 InventoryList * Inventory::getList(const std::string &name)
974 s32 i = getListIndex(name);
980 bool Inventory::deleteList(const std::string &name)
982 s32 i = getListIndex(name);
990 const InventoryList * Inventory::getList(const std::string &name) const
992 s32 i = getListIndex(name);
998 const s32 Inventory::getListIndex(const std::string &name) const
1000 for(u32 i=0; i<m_lists.size(); i++)
1002 if(m_lists[i]->getName() == name)
1012 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1015 std::getline(is, type, ' ');
1017 InventoryAction *a = NULL;
1021 a = new IMoveAction(is);
1023 else if(type == "Drop")
1025 a = new IDropAction(is);
1031 static std::string describeC(const struct InventoryContext *c)
1033 if(c->current_player == NULL)
1034 return "current_player=NULL";
1036 return std::string("current_player=") + c->current_player->getName();
1039 IMoveAction::IMoveAction(std::istream &is)
1043 std::getline(is, ts, ' ');
1046 std::getline(is, from_inv, ' ');
1048 std::getline(is, from_list, ' ');
1050 std::getline(is, ts, ' ');
1053 std::getline(is, to_inv, ' ');
1055 std::getline(is, to_list, ' ');
1057 std::getline(is, ts, ' ');
1061 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1062 ServerEnvironment *env)
1064 Inventory *inv_from = mgr->getInventory(c, from_inv);
1065 Inventory *inv_to = mgr->getInventory(c, to_inv);
1068 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1069 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1070 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1074 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1075 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1076 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1080 InventoryList *list_from = inv_from->getList(from_list);
1081 InventoryList *list_to = inv_to->getList(to_list);
1084 If a list doesn't exist or the source item doesn't exist
1087 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1088 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1089 <<", from_list=\""<<from_list<<"\""<<std::endl;
1093 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1094 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1095 <<", to_list=\""<<to_list<<"\""<<std::endl;
1098 if(list_from->getItem(from_i) == NULL)
1100 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1101 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1102 <<", from_list=\""<<from_list<<"\""
1103 <<" from_i="<<from_i<<std::endl;
1107 If the source and the destination slots are the same
1109 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1111 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1112 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1113 <<"\" i="<<from_i<<std::endl;
1117 // Take item from source list
1118 InventoryItem *item1 = NULL;
1120 item1 = list_from->changeItem(from_i, NULL);
1122 item1 = list_from->takeItem(from_i, count);
1124 // Try to add the item to destination list
1125 InventoryItem *olditem = item1;
1126 item1 = list_to->addItem(to_i, item1);
1128 // If something is returned, the item was not fully added
1131 // If olditem is returned, nothing was added.
1132 bool nothing_added = (item1 == olditem);
1134 // If something else is returned, part of the item was left unadded.
1135 // Add the other part back to the source item
1136 list_from->addItem(from_i, item1);
1138 // If olditem is returned, nothing was added.
1142 // Take item from source list
1143 item1 = list_from->changeItem(from_i, NULL);
1144 // Adding was not possible, swap the items.
1145 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1146 // Put item from destination list to the source list
1147 list_from->changeItem(from_i, item2);
1151 mgr->inventoryModified(c, from_inv);
1152 if(from_inv != to_inv)
1153 mgr->inventoryModified(c, to_inv);
1155 infostream<<"IMoveAction::apply(): moved at "
1156 <<"["<<describeC(c)<<"]"
1157 <<" from inv=\""<<from_inv<<"\""
1158 <<" list=\""<<from_list<<"\""
1160 <<" to inv=\""<<to_inv<<"\""
1161 <<" list=\""<<to_list<<"\""
1166 IDropAction::IDropAction(std::istream &is)
1170 std::getline(is, ts, ' ');
1173 std::getline(is, from_inv, ' ');
1175 std::getline(is, from_list, ' ');
1177 std::getline(is, ts, ' ');
1181 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1182 ServerEnvironment *env)
1184 if(c->current_player == NULL){
1185 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1189 // Do NOT cast directly to ServerActiveObject*, it breaks
1190 // because of multiple inheritance.
1191 ServerActiveObject *dropper =
1192 static_cast<ServerActiveObject*>(
1193 static_cast<ServerRemotePlayer*>(
1197 Inventory *inv_from = mgr->getInventory(c, from_inv);
1200 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1201 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1205 InventoryList *list_from = inv_from->getList(from_list);
1208 If a list doesn't exist or the source item doesn't exist
1211 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1212 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1213 <<", from_list=\""<<from_list<<"\""<<std::endl;
1216 InventoryItem *item = list_from->getItem(from_i);
1219 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1220 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1221 <<", from_list=\""<<from_list<<"\""
1222 <<" from_i="<<from_i<<std::endl;
1226 v3f pos = dropper->getBasePosition();
1236 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1238 list_from->deleteItem(from_i);
1240 mgr->inventoryModified(c, from_inv);
1242 infostream<<"IDropAction::apply(): dropped "
1243 <<"["<<describeC(c)<<"]"
1244 <<" from inv=\""<<from_inv<<"\""
1245 <<" list=\""<<from_list<<"\""
1251 Craft checking system
1254 bool ItemSpec::checkItem(const InventoryItem *item) const
1256 if(type == ITEM_NONE)
1258 // Has to be no item
1264 // There should be an item
1268 std::string itemname = item->getName();
1270 if(type == ITEM_MATERIAL)
1272 if(itemname != "MaterialItem")
1274 MaterialItem *mitem = (MaterialItem*)item;
1276 if(mitem->getMaterial() != num)
1279 if(mitem->getNodeName() != name)
1283 else if(type == ITEM_CRAFT)
1285 if(itemname != "CraftItem")
1287 CraftItem *mitem = (CraftItem*)item;
1288 if(mitem->getSubName() != name)
1291 else if(type == ITEM_TOOL)
1293 // Not supported yet
1296 else if(type == ITEM_MBO)
1298 // Not supported yet
1303 // Not supported yet
1309 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1311 u16 items_min_x = 100;
1312 u16 items_max_x = 100;
1313 u16 items_min_y = 100;
1314 u16 items_max_y = 100;
1315 for(u16 y=0; y<3; y++)
1316 for(u16 x=0; x<3; x++)
1318 if(items[y*3 + x] == NULL)
1320 if(items_min_x == 100 || x < items_min_x)
1322 if(items_min_y == 100 || y < items_min_y)
1324 if(items_max_x == 100 || x > items_max_x)
1326 if(items_max_y == 100 || y > items_max_y)
1329 // No items at all, just return false
1330 if(items_min_x == 100)
1333 u16 items_w = items_max_x - items_min_x + 1;
1334 u16 items_h = items_max_y - items_min_y + 1;
1336 u16 specs_min_x = 100;
1337 u16 specs_max_x = 100;
1338 u16 specs_min_y = 100;
1339 u16 specs_max_y = 100;
1340 for(u16 y=0; y<3; y++)
1341 for(u16 x=0; x<3; x++)
1343 if(specs[y*3 + x].type == ITEM_NONE)
1345 if(specs_min_x == 100 || x < specs_min_x)
1347 if(specs_min_y == 100 || y < specs_min_y)
1349 if(specs_max_x == 100 || x > specs_max_x)
1351 if(specs_max_y == 100 || y > specs_max_y)
1354 // No specs at all, just return false
1355 if(specs_min_x == 100)
1358 u16 specs_w = specs_max_x - specs_min_x + 1;
1359 u16 specs_h = specs_max_y - specs_min_y + 1;
1362 if(items_w != specs_w || items_h != specs_h)
1365 for(u16 y=0; y<specs_h; y++)
1366 for(u16 x=0; x<specs_w; x++)
1368 u16 items_x = items_min_x + x;
1369 u16 items_y = items_min_y + y;
1370 u16 specs_x = specs_min_x + x;
1371 u16 specs_y = specs_min_y + y;
1372 const InventoryItem *item = items[items_y * 3 + items_x];
1373 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1375 if(spec.checkItem(item) == false)
1382 bool checkItemCombination(const InventoryItem * const * items,
1383 const InventoryItem * const * specs)
1385 u16 items_min_x = 100;
1386 u16 items_max_x = 100;
1387 u16 items_min_y = 100;
1388 u16 items_max_y = 100;
1389 for(u16 y=0; y<3; y++)
1390 for(u16 x=0; x<3; x++)
1392 if(items[y*3 + x] == NULL)
1394 if(items_min_x == 100 || x < items_min_x)
1396 if(items_min_y == 100 || y < items_min_y)
1398 if(items_max_x == 100 || x > items_max_x)
1400 if(items_max_y == 100 || y > items_max_y)
1403 // No items at all, just return false
1404 if(items_min_x == 100)
1407 u16 items_w = items_max_x - items_min_x + 1;
1408 u16 items_h = items_max_y - items_min_y + 1;
1410 u16 specs_min_x = 100;
1411 u16 specs_max_x = 100;
1412 u16 specs_min_y = 100;
1413 u16 specs_max_y = 100;
1414 for(u16 y=0; y<3; y++)
1415 for(u16 x=0; x<3; x++)
1417 if(specs[y*3 + x] == NULL)
1419 if(specs_min_x == 100 || x < specs_min_x)
1421 if(specs_min_y == 100 || y < specs_min_y)
1423 if(specs_max_x == 100 || x > specs_max_x)
1425 if(specs_max_y == 100 || y > specs_max_y)
1428 // No specs at all, just return false
1429 if(specs_min_x == 100)
1432 u16 specs_w = specs_max_x - specs_min_x + 1;
1433 u16 specs_h = specs_max_y - specs_min_y + 1;
1436 if(items_w != specs_w || items_h != specs_h)
1439 for(u16 y=0; y<specs_h; y++)
1440 for(u16 x=0; x<specs_w; x++)
1442 u16 items_x = items_min_x + x;
1443 u16 items_y = items_min_y + y;
1444 u16 specs_x = specs_min_x + x;
1445 u16 specs_y = specs_min_y + y;
1446 const InventoryItem *item = items[items_y * 3 + items_x];
1447 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1449 if(item == NULL && spec == NULL)
1451 if(item == NULL && spec != NULL)
1453 if(item != NULL && spec == NULL)
1455 if(!spec->isSubsetOf(item))