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("")));
117 return new MaterialItem(gamedef, nodename, count);
119 else if(name == "MBOItem")
121 std::string inventorystring;
122 std::getline(is, inventorystring, '|');
123 throw SerializationError("MBOItem not supported anymore");
125 else if(name == "CraftItem")
128 std::getline(is, all, '\n');
130 // First attempt to read inside ""
133 // If didn't skip to end, we have ""s
135 subname = fnd.next("\"");
136 } else { // No luck, just read a word then
138 subname = fnd.next(" ");
142 u16 count = stoi(trim(fnd.next("")));
143 return new CraftItem(gamedef, subname, count);
145 else if(name == "ToolItem")
148 std::getline(is, all, '\n');
149 std::string toolname;
150 // First attempt to read inside ""
153 // If didn't skip to end, we have ""s
155 toolname = fnd.next("\"");
156 } else { // No luck, just read a word then
158 toolname = fnd.next(" ");
162 u16 wear = stoi(trim(fnd.next("")));
163 return new ToolItem(gamedef, toolname, wear);
167 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
168 throw SerializationError("Unknown InventoryItem name");
172 InventoryItem* InventoryItem::deSerialize(const std::string &str,
175 std::istringstream is(str, std::ios_base::binary);
176 return deSerialize(is, gamedef);
179 std::string InventoryItem::getItemString() {
181 std::ostringstream os(std::ios_base::binary);
186 bool InventoryItem::dropOrPlace(ServerEnvironment *env,
187 ServerActiveObject *dropper,
188 v3f pos, bool place, s16 count)
191 Ensure that the block is loaded so that the item
192 can properly be added to the static list too
194 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
195 MapBlock *block = env->getMap().emergeBlock(blockpos, false);
198 infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: "
199 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
205 Take specified number of items,
206 but limit to getDropCount().
208 s16 dropcount = getDropCount();
209 if(count < 0 || count > dropcount)
211 if(count < 0 || count > getCount());
218 pos.Y -= BS*0.25; // let it drop a bit
220 //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
221 //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
223 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
224 // Add the object to the environment
225 env->addActiveObject(obj);
226 infostream<<"Dropped item"<<std::endl;
228 setCount(getCount() - count);
231 return getCount() < 1; // delete the item?
238 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
239 InventoryItem(gamedef, count)
242 nodename = "unknown_block";
243 m_nodename = nodename;
245 // Legacy constructor
246 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
247 InventoryItem(gamedef, count)
249 INodeDefManager *ndef = m_gamedef->ndef();
250 std::string nodename = ndef->get(content).name;
252 nodename = "unknown_block";
253 m_nodename = nodename;
257 video::ITexture * MaterialItem::getImage() const
259 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
263 bool MaterialItem::isCookable() const
265 INodeDefManager *ndef = m_gamedef->ndef();
266 const ContentFeatures &f = ndef->get(m_nodename);
267 return (f.cookresult_item != "");
270 InventoryItem *MaterialItem::createCookResult() const
272 INodeDefManager *ndef = m_gamedef->ndef();
273 const ContentFeatures &f = ndef->get(m_nodename);
274 std::istringstream is(f.cookresult_item, std::ios::binary);
275 return InventoryItem::deSerialize(is, m_gamedef);
278 float MaterialItem::getCookTime() const
280 INodeDefManager *ndef = m_gamedef->ndef();
281 const ContentFeatures &f = ndef->get(m_nodename);
282 return f.furnace_cooktime;
285 float MaterialItem::getBurnTime() const
287 INodeDefManager *ndef = m_gamedef->ndef();
288 const ContentFeatures &f = ndef->get(m_nodename);
289 return f.furnace_burntime;
292 content_t MaterialItem::getMaterial() const
294 INodeDefManager *ndef = m_gamedef->ndef();
295 content_t id = CONTENT_IGNORE;
296 ndef->getId(m_nodename, id);
304 std::string ToolItem::getImageBasename() const
306 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
310 video::ITexture * ToolItem::getImage() const
312 ITextureSource *tsrc = m_gamedef->tsrc();
314 std::string basename = getImageBasename();
317 Calculate a progress value with sane amount of
320 u32 maxprogress = 30;
321 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
323 float value_f = (float)toolprogress / (float)maxprogress;
324 std::ostringstream os;
325 os<<basename<<"^[progressbar"<<value_f;
327 return tsrc->getTextureRaw(os.str());
330 video::ITexture * ToolItem::getImageRaw() const
332 ITextureSource *tsrc = m_gamedef->tsrc();
334 return tsrc->getTextureRaw(getImageBasename());
343 video::ITexture * CraftItem::getImage() const
345 ICraftItemDefManager *cidef = m_gamedef->cidef();
346 ITextureSource *tsrc = m_gamedef->tsrc();
347 std::string imagename = cidef->getImagename(m_subname);
348 return tsrc->getTextureRaw(imagename);
352 u16 CraftItem::getStackMax() const
354 ICraftItemDefManager *cidef = m_gamedef->cidef();
355 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
357 return InventoryItem::getStackMax();
358 return def->stack_max;
361 bool CraftItem::isUsable() const
363 ICraftItemDefManager *cidef = m_gamedef->cidef();
364 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
365 return def != NULL && def->usable;
368 bool CraftItem::isCookable() const
370 ICraftItemDefManager *cidef = m_gamedef->cidef();
371 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
372 return def != NULL && def->cookresult_item != "";
375 InventoryItem *CraftItem::createCookResult() const
377 ICraftItemDefManager *cidef = m_gamedef->cidef();
378 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
380 return InventoryItem::createCookResult();
381 std::istringstream is(def->cookresult_item, std::ios::binary);
382 return InventoryItem::deSerialize(is, m_gamedef);
385 float CraftItem::getCookTime() const
387 ICraftItemDefManager *cidef = m_gamedef->cidef();
388 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
390 return InventoryItem::getCookTime();
391 return def->furnace_cooktime;
394 float CraftItem::getBurnTime() const
396 ICraftItemDefManager *cidef = m_gamedef->cidef();
397 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
399 return InventoryItem::getBurnTime();
400 return def->furnace_burntime;
403 s16 CraftItem::getDropCount() const
406 ICraftItemDefManager *cidef = m_gamedef->cidef();
407 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
408 if(def != NULL && def->dropcount >= 0)
409 return def->dropcount;
411 return InventoryItem::getDropCount();
414 bool CraftItem::areLiquidsPointable() const
416 ICraftItemDefManager *cidef = m_gamedef->cidef();
417 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
418 return def != NULL && def->liquids_pointable;
421 bool CraftItem::dropOrPlace(ServerEnvironment *env,
422 ServerActiveObject *dropper,
423 v3f pos, bool place, s16 count)
428 bool callback_exists = false;
433 result = scriptapi_craftitem_on_place_on_ground(
435 m_subname.c_str(), dropper, pos,
439 // note: on_drop is fallback for on_place_on_ground
443 result = scriptapi_craftitem_on_drop(
445 m_subname.c_str(), dropper, pos,
451 // If the callback returned true, drop one item
453 setCount(getCount() - 1);
454 return getCount() < 1;
458 // If neither on_place_on_ground (if place==true)
459 // nor on_drop exists, call the base implementation
460 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
464 bool CraftItem::use(ServerEnvironment *env,
465 ServerActiveObject *user,
466 const PointedThing& pointed)
468 bool callback_exists = false;
471 result = scriptapi_craftitem_on_use(
473 m_subname.c_str(), user, pointed,
478 // If the callback returned true, drop one item
480 setCount(getCount() - 1);
481 return getCount() < 1;
485 // If neither on_place_on_ground (if place==true)
486 // nor on_drop exists, call the base implementation
487 return InventoryItem::use(env, user, pointed);
495 InventoryList::InventoryList(std::string name, u32 size)
503 InventoryList::~InventoryList()
505 for(u32 i=0; i<m_items.size(); i++)
512 void InventoryList::clearItems()
514 for(u32 i=0; i<m_items.size(); i++)
522 for(u32 i=0; i<m_size; i++)
524 m_items.push_back(NULL);
530 void InventoryList::serialize(std::ostream &os) const
532 //os.imbue(std::locale("C"));
534 for(u32 i=0; i<m_items.size(); i++)
536 InventoryItem *item = m_items[i];
549 os<<"EndInventoryList\n";
552 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
554 //is.imbue(std::locale("C"));
562 std::getline(is, line, '\n');
564 std::istringstream iss(line);
565 //iss.imbue(std::locale("C"));
568 std::getline(iss, name, ' ');
570 if(name == "EndInventoryList")
574 // This is a temporary backwards compatibility fix
575 else if(name == "end")
579 else if(name == "Item")
581 if(item_i > getSize() - 1)
582 throw SerializationError("too many items");
583 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
584 m_items[item_i++] = item;
586 else if(name == "Empty")
588 if(item_i > getSize() - 1)
589 throw SerializationError("too many items");
590 m_items[item_i++] = NULL;
594 throw SerializationError("Unknown inventory identifier");
599 InventoryList::InventoryList(const InventoryList &other)
602 Do this so that the items get cloned. Otherwise the pointers
603 in the array will just get copied.
608 InventoryList & InventoryList::operator = (const InventoryList &other)
610 m_name = other.m_name;
611 m_size = other.m_size;
613 for(u32 i=0; i<other.m_items.size(); i++)
615 InventoryItem *item = other.m_items[i];
618 m_items[i] = item->clone();
626 const std::string &InventoryList::getName() const
631 u32 InventoryList::getSize()
633 return m_items.size();
636 u32 InventoryList::getUsedSlots()
639 for(u32 i=0; i<m_items.size(); i++)
641 InventoryItem *item = m_items[i];
648 u32 InventoryList::getFreeSlots()
650 return getSize() - getUsedSlots();
653 const InventoryItem * InventoryList::getItem(u32 i) const
655 if(i >= m_items.size())
660 InventoryItem * InventoryList::getItem(u32 i)
662 if(i >= m_items.size())
667 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
669 if(i >= m_items.size())
672 InventoryItem *olditem = m_items[i];
673 m_items[i] = newitem;
678 void InventoryList::deleteItem(u32 i)
680 assert(i < m_items.size());
681 InventoryItem *item = changeItem(i, NULL);
686 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
692 First try to find if it could be added to some existing items
694 for(u32 i=0; i<m_items.size(); i++)
696 // Ignore empty slots
697 if(m_items[i] == NULL)
700 newitem = addItem(i, newitem);
702 return NULL; // All was eaten
706 Then try to add it to empty slots
708 for(u32 i=0; i<m_items.size(); i++)
710 // Ignore unempty slots
711 if(m_items[i] != NULL)
714 newitem = addItem(i, newitem);
716 return NULL; // All was eaten
723 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
727 if(i >= m_items.size())
732 // If it is an empty position, it's an easy job.
733 InventoryItem *to_item = getItem(i);
736 m_items[i] = newitem;
740 // If not addable, return the item
741 if(newitem->addableTo(to_item) == false)
744 // If the item fits fully in the slot, add counter and delete it
745 if(newitem->getCount() <= to_item->freeSpace())
747 to_item->add(newitem->getCount());
751 // Else the item does not fit fully. Add all that fits and return
755 u16 freespace = to_item->freeSpace();
756 to_item->add(freespace);
757 newitem->remove(freespace);
762 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
764 // If it is an empty position, it's an easy job.
765 const InventoryItem *to_item = getItem(i);
771 // If not addable, fail
772 if(newitem->addableTo(to_item) == false)
775 // If the item fits fully in the slot, pass
776 if(newitem->getCount() <= to_item->freeSpace())
784 bool InventoryList::roomForItem(const InventoryItem *item)
786 for(u32 i=0; i<m_items.size(); i++)
787 if(itemFits(i, item))
792 bool InventoryList::roomForCookedItem(const InventoryItem *item)
796 const InventoryItem *cook = item->createCookResult();
799 bool room = roomForItem(cook);
804 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
811 InventoryItem *item = getItem(i);
812 // If it is an empty position, return NULL
816 if(count >= item->getCount())
818 // Get the item by swapping NULL to its place
819 return changeItem(i, NULL);
823 InventoryItem *item2 = item->clone();
825 item2->setCount(count);
832 void InventoryList::decrementMaterials(u16 count)
834 for(u32 i=0; i<m_items.size(); i++)
836 InventoryItem *item = takeItem(i, count);
842 void InventoryList::print(std::ostream &o)
844 o<<"InventoryList:"<<std::endl;
845 for(u32 i=0; i<m_items.size(); i++)
847 InventoryItem *item = m_items[i];
861 Inventory::~Inventory()
866 void Inventory::clear()
868 for(u32 i=0; i<m_lists.size(); i++)
875 Inventory::Inventory()
879 Inventory::Inventory(const Inventory &other)
884 Inventory & Inventory::operator = (const Inventory &other)
887 for(u32 i=0; i<other.m_lists.size(); i++)
889 m_lists.push_back(new InventoryList(*other.m_lists[i]));
894 void Inventory::serialize(std::ostream &os) const
896 for(u32 i=0; i<m_lists.size(); i++)
898 InventoryList *list = m_lists[i];
899 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
903 os<<"EndInventory\n";
906 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
913 std::getline(is, line, '\n');
915 std::istringstream iss(line);
918 std::getline(iss, name, ' ');
920 if(name == "EndInventory")
924 // This is a temporary backwards compatibility fix
925 else if(name == "end")
929 else if(name == "List")
931 std::string listname;
934 std::getline(iss, listname, ' ');
937 InventoryList *list = new InventoryList(listname, listsize);
938 list->deSerialize(is, gamedef);
940 m_lists.push_back(list);
944 throw SerializationError("Unknown inventory identifier");
949 InventoryList * Inventory::addList(const std::string &name, u32 size)
951 s32 i = getListIndex(name);
954 if(m_lists[i]->getSize() != size)
957 m_lists[i] = new InventoryList(name, size);
963 m_lists.push_back(new InventoryList(name, size));
964 return m_lists.getLast();
968 InventoryList * Inventory::getList(const std::string &name)
970 s32 i = getListIndex(name);
976 bool Inventory::deleteList(const std::string &name)
978 s32 i = getListIndex(name);
986 const InventoryList * Inventory::getList(const std::string &name) const
988 s32 i = getListIndex(name);
994 const s32 Inventory::getListIndex(const std::string &name) const
996 for(u32 i=0; i<m_lists.size(); i++)
998 if(m_lists[i]->getName() == name)
1008 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1011 std::getline(is, type, ' ');
1013 InventoryAction *a = NULL;
1017 a = new IMoveAction(is);
1019 else if(type == "Drop")
1021 a = new IDropAction(is);
1027 static std::string describeC(const struct InventoryContext *c)
1029 if(c->current_player == NULL)
1030 return "current_player=NULL";
1032 return std::string("current_player=") + c->current_player->getName();
1035 IMoveAction::IMoveAction(std::istream &is)
1039 std::getline(is, ts, ' ');
1042 std::getline(is, from_inv, ' ');
1044 std::getline(is, from_list, ' ');
1046 std::getline(is, ts, ' ');
1049 std::getline(is, to_inv, ' ');
1051 std::getline(is, to_list, ' ');
1053 std::getline(is, ts, ' ');
1057 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1058 ServerEnvironment *env)
1060 Inventory *inv_from = mgr->getInventory(c, from_inv);
1061 Inventory *inv_to = mgr->getInventory(c, to_inv);
1064 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1065 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1066 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1070 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1071 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1072 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1076 InventoryList *list_from = inv_from->getList(from_list);
1077 InventoryList *list_to = inv_to->getList(to_list);
1080 If a list doesn't exist or the source item doesn't exist
1083 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1084 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1085 <<", from_list=\""<<from_list<<"\""<<std::endl;
1089 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1090 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1091 <<", to_list=\""<<to_list<<"\""<<std::endl;
1094 if(list_from->getItem(from_i) == NULL)
1096 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1097 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1098 <<", from_list=\""<<from_list<<"\""
1099 <<" from_i="<<from_i<<std::endl;
1103 If the source and the destination slots are the same
1105 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1107 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1108 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1109 <<"\" i="<<from_i<<std::endl;
1113 // Take item from source list
1114 InventoryItem *item1 = NULL;
1116 item1 = list_from->changeItem(from_i, NULL);
1118 item1 = list_from->takeItem(from_i, count);
1120 // Try to add the item to destination list
1121 InventoryItem *olditem = item1;
1122 item1 = list_to->addItem(to_i, item1);
1124 // If something is returned, the item was not fully added
1127 // If olditem is returned, nothing was added.
1128 bool nothing_added = (item1 == olditem);
1130 // If something else is returned, part of the item was left unadded.
1131 // Add the other part back to the source item
1132 list_from->addItem(from_i, item1);
1134 // If olditem is returned, nothing was added.
1138 // Take item from source list
1139 item1 = list_from->changeItem(from_i, NULL);
1140 // Adding was not possible, swap the items.
1141 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1142 // Put item from destination list to the source list
1143 list_from->changeItem(from_i, item2);
1147 mgr->inventoryModified(c, from_inv);
1148 if(from_inv != to_inv)
1149 mgr->inventoryModified(c, to_inv);
1151 infostream<<"IMoveAction::apply(): moved at "
1152 <<"["<<describeC(c)<<"]"
1153 <<" from inv=\""<<from_inv<<"\""
1154 <<" list=\""<<from_list<<"\""
1156 <<" to inv=\""<<to_inv<<"\""
1157 <<" list=\""<<to_list<<"\""
1162 IDropAction::IDropAction(std::istream &is)
1166 std::getline(is, ts, ' ');
1169 std::getline(is, from_inv, ' ');
1171 std::getline(is, from_list, ' ');
1173 std::getline(is, ts, ' ');
1177 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1178 ServerEnvironment *env)
1180 if(c->current_player == NULL){
1181 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1185 // Do NOT cast directly to ServerActiveObject*, it breaks
1186 // because of multiple inheritance.
1187 ServerActiveObject *dropper =
1188 static_cast<ServerActiveObject*>(
1189 static_cast<ServerRemotePlayer*>(
1193 Inventory *inv_from = mgr->getInventory(c, from_inv);
1196 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1197 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1201 InventoryList *list_from = inv_from->getList(from_list);
1204 If a list doesn't exist or the source item doesn't exist
1207 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1208 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1209 <<", from_list=\""<<from_list<<"\""<<std::endl;
1212 InventoryItem *item = list_from->getItem(from_i);
1215 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1216 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1217 <<", from_list=\""<<from_list<<"\""
1218 <<" from_i="<<from_i<<std::endl;
1222 v3f pos = dropper->getBasePosition();
1232 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1234 list_from->deleteItem(from_i);
1236 mgr->inventoryModified(c, from_inv);
1238 infostream<<"IDropAction::apply(): dropped "
1239 <<"["<<describeC(c)<<"]"
1240 <<" from inv=\""<<from_inv<<"\""
1241 <<" list=\""<<from_list<<"\""
1247 Craft checking system
1250 bool ItemSpec::checkItem(const InventoryItem *item) const
1252 if(type == ITEM_NONE)
1254 // Has to be no item
1260 // There should be an item
1264 std::string itemname = item->getName();
1266 if(type == ITEM_MATERIAL)
1268 if(itemname != "MaterialItem")
1270 MaterialItem *mitem = (MaterialItem*)item;
1272 if(mitem->getMaterial() != num)
1275 if(mitem->getNodeName() != name)
1279 else if(type == ITEM_CRAFT)
1281 if(itemname != "CraftItem")
1283 CraftItem *mitem = (CraftItem*)item;
1284 if(mitem->getSubName() != name)
1287 else if(type == ITEM_TOOL)
1289 // Not supported yet
1292 else if(type == ITEM_MBO)
1294 // Not supported yet
1299 // Not supported yet
1305 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1307 u16 items_min_x = 100;
1308 u16 items_max_x = 100;
1309 u16 items_min_y = 100;
1310 u16 items_max_y = 100;
1311 for(u16 y=0; y<3; y++)
1312 for(u16 x=0; x<3; x++)
1314 if(items[y*3 + x] == NULL)
1316 if(items_min_x == 100 || x < items_min_x)
1318 if(items_min_y == 100 || y < items_min_y)
1320 if(items_max_x == 100 || x > items_max_x)
1322 if(items_max_y == 100 || y > items_max_y)
1325 // No items at all, just return false
1326 if(items_min_x == 100)
1329 u16 items_w = items_max_x - items_min_x + 1;
1330 u16 items_h = items_max_y - items_min_y + 1;
1332 u16 specs_min_x = 100;
1333 u16 specs_max_x = 100;
1334 u16 specs_min_y = 100;
1335 u16 specs_max_y = 100;
1336 for(u16 y=0; y<3; y++)
1337 for(u16 x=0; x<3; x++)
1339 if(specs[y*3 + x].type == ITEM_NONE)
1341 if(specs_min_x == 100 || x < specs_min_x)
1343 if(specs_min_y == 100 || y < specs_min_y)
1345 if(specs_max_x == 100 || x > specs_max_x)
1347 if(specs_max_y == 100 || y > specs_max_y)
1350 // No specs at all, just return false
1351 if(specs_min_x == 100)
1354 u16 specs_w = specs_max_x - specs_min_x + 1;
1355 u16 specs_h = specs_max_y - specs_min_y + 1;
1358 if(items_w != specs_w || items_h != specs_h)
1361 for(u16 y=0; y<specs_h; y++)
1362 for(u16 x=0; x<specs_w; x++)
1364 u16 items_x = items_min_x + x;
1365 u16 items_y = items_min_y + y;
1366 u16 specs_x = specs_min_x + x;
1367 u16 specs_y = specs_min_y + y;
1368 const InventoryItem *item = items[items_y * 3 + items_x];
1369 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1371 if(spec.checkItem(item) == false)
1378 bool checkItemCombination(const InventoryItem * const * items,
1379 const InventoryItem * const * specs)
1381 u16 items_min_x = 100;
1382 u16 items_max_x = 100;
1383 u16 items_min_y = 100;
1384 u16 items_max_y = 100;
1385 for(u16 y=0; y<3; y++)
1386 for(u16 x=0; x<3; x++)
1388 if(items[y*3 + x] == NULL)
1390 if(items_min_x == 100 || x < items_min_x)
1392 if(items_min_y == 100 || y < items_min_y)
1394 if(items_max_x == 100 || x > items_max_x)
1396 if(items_max_y == 100 || y > items_max_y)
1399 // No items at all, just return false
1400 if(items_min_x == 100)
1403 u16 items_w = items_max_x - items_min_x + 1;
1404 u16 items_h = items_max_y - items_min_y + 1;
1406 u16 specs_min_x = 100;
1407 u16 specs_max_x = 100;
1408 u16 specs_min_y = 100;
1409 u16 specs_max_y = 100;
1410 for(u16 y=0; y<3; y++)
1411 for(u16 x=0; x<3; x++)
1413 if(specs[y*3 + x] == NULL)
1415 if(specs_min_x == 100 || x < specs_min_x)
1417 if(specs_min_y == 100 || y < specs_min_y)
1419 if(specs_max_x == 100 || x > specs_max_x)
1421 if(specs_max_y == 100 || y > specs_max_y)
1424 // No specs at all, just return false
1425 if(specs_min_x == 100)
1428 u16 specs_w = specs_max_x - specs_min_x + 1;
1429 u16 specs_h = specs_max_y - specs_min_y + 1;
1432 if(items_w != specs_w || items_h != specs_h)
1435 for(u16 y=0; y<specs_h; y++)
1436 for(u16 x=0; x<specs_w; x++)
1438 u16 items_x = items_min_x + x;
1439 u16 items_y = items_min_y + y;
1440 u16 specs_x = specs_min_x + x;
1441 u16 specs_y = specs_min_y + y;
1442 const InventoryItem *item = items[items_y * 3 + items_x];
1443 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1445 if(item == NULL && spec == NULL)
1447 if(item == NULL && spec != NULL)
1449 if(item != NULL && spec == NULL)
1451 if(!spec->isSubsetOf(item))