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"
39 #include "nameidmapping.h" // For loading legacy MaterialItems
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("")));
120 return new MaterialItem(gamedef, nodename, count);
122 else if(name == "MBOItem")
124 std::string inventorystring;
125 std::getline(is, inventorystring, '|');
126 throw SerializationError("MBOItem not supported anymore");
128 else if(name == "CraftItem")
131 std::getline(is, all, '\n');
133 // First attempt to read inside ""
136 // If didn't skip to end, we have ""s
138 subname = fnd.next("\"");
139 } else { // No luck, just read a word then
141 subname = fnd.next(" ");
145 u16 count = stoi(trim(fnd.next("")));
148 return new CraftItem(gamedef, subname, count);
150 else if(name == "ToolItem")
153 std::getline(is, all, '\n');
154 std::string toolname;
155 // First attempt to read inside ""
158 // If didn't skip to end, we have ""s
160 toolname = fnd.next("\"");
161 } else { // No luck, just read a word then
163 toolname = fnd.next(" ");
167 u16 wear = stoi(trim(fnd.next("")));
168 return new ToolItem(gamedef, toolname, wear);
172 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
173 throw SerializationError("Unknown InventoryItem name");
177 InventoryItem* InventoryItem::deSerialize(const std::string &str,
180 std::istringstream is(str, std::ios_base::binary);
181 return deSerialize(is, gamedef);
184 std::string InventoryItem::getItemString() {
186 std::ostringstream os(std::ios_base::binary);
191 bool InventoryItem::dropOrPlace(ServerEnvironment *env,
192 ServerActiveObject *dropper,
193 v3f pos, bool place, s16 count)
196 Ensure that the block is loaded so that the item
197 can properly be added to the static list too
199 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
200 MapBlock *block = env->getMap().emergeBlock(blockpos, false);
203 infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: "
204 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
210 Take specified number of items,
211 but limit to getDropCount().
213 s16 dropcount = getDropCount();
214 if(count < 0 || count > dropcount)
216 if(count < 0 || count > getCount())
223 pos.Y -= BS*0.25; // let it drop a bit
225 //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
226 //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
228 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
229 // Add the object to the environment
230 env->addActiveObject(obj);
231 infostream<<"Dropped item"<<std::endl;
233 setCount(getCount() - count);
236 return getCount() < 1; // delete the item?
243 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
244 InventoryItem(gamedef, count)
247 nodename = "unknown_block";
248 m_nodename = nodename;
250 // Legacy constructor
251 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
252 InventoryItem(gamedef, count)
254 NameIdMapping legacy_nimap;
255 content_mapnode_get_name_id_mapping(&legacy_nimap);
256 std::string nodename;
257 legacy_nimap.getName(content, nodename);
259 nodename = "unknown_block";
260 m_nodename = nodename;
264 video::ITexture * MaterialItem::getImage() const
266 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
270 bool MaterialItem::isCookable() const
272 INodeDefManager *ndef = m_gamedef->ndef();
273 const ContentFeatures &f = ndef->get(m_nodename);
274 return (f.cookresult_item != "");
277 InventoryItem *MaterialItem::createCookResult() const
279 INodeDefManager *ndef = m_gamedef->ndef();
280 const ContentFeatures &f = ndef->get(m_nodename);
281 std::istringstream is(f.cookresult_item, std::ios::binary);
282 return InventoryItem::deSerialize(is, m_gamedef);
285 float MaterialItem::getCookTime() const
287 INodeDefManager *ndef = m_gamedef->ndef();
288 const ContentFeatures &f = ndef->get(m_nodename);
289 return f.furnace_cooktime;
292 float MaterialItem::getBurnTime() const
294 INodeDefManager *ndef = m_gamedef->ndef();
295 const ContentFeatures &f = ndef->get(m_nodename);
296 return f.furnace_burntime;
299 content_t MaterialItem::getMaterial() const
301 INodeDefManager *ndef = m_gamedef->ndef();
302 content_t id = CONTENT_IGNORE;
303 ndef->getId(m_nodename, id);
311 std::string ToolItem::getImageBasename() const
313 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
317 video::ITexture * ToolItem::getImage() const
319 ITextureSource *tsrc = m_gamedef->tsrc();
321 std::string basename = getImageBasename();
324 Calculate a progress value with sane amount of
327 u32 maxprogress = 30;
328 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
330 float value_f = (float)toolprogress / (float)maxprogress;
331 std::ostringstream os;
332 os<<basename<<"^[progressbar"<<value_f;
334 return tsrc->getTextureRaw(os.str());
337 video::ITexture * ToolItem::getImageRaw() const
339 ITextureSource *tsrc = m_gamedef->tsrc();
341 return tsrc->getTextureRaw(getImageBasename());
350 video::ITexture * CraftItem::getImage() const
352 ICraftItemDefManager *cidef = m_gamedef->cidef();
353 ITextureSource *tsrc = m_gamedef->tsrc();
354 std::string imagename = cidef->getImagename(m_subname);
355 return tsrc->getTextureRaw(imagename);
359 u16 CraftItem::getStackMax() const
361 ICraftItemDefManager *cidef = m_gamedef->cidef();
362 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
364 return InventoryItem::getStackMax();
365 return def->stack_max;
368 bool CraftItem::isUsable() const
370 ICraftItemDefManager *cidef = m_gamedef->cidef();
371 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
372 return def != NULL && def->usable;
375 bool CraftItem::isCookable() const
377 ICraftItemDefManager *cidef = m_gamedef->cidef();
378 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
379 return def != NULL && def->cookresult_item != "";
382 InventoryItem *CraftItem::createCookResult() const
384 ICraftItemDefManager *cidef = m_gamedef->cidef();
385 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
387 return InventoryItem::createCookResult();
388 std::istringstream is(def->cookresult_item, std::ios::binary);
389 return InventoryItem::deSerialize(is, m_gamedef);
392 float CraftItem::getCookTime() const
394 ICraftItemDefManager *cidef = m_gamedef->cidef();
395 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
397 return InventoryItem::getCookTime();
398 return def->furnace_cooktime;
401 float CraftItem::getBurnTime() const
403 ICraftItemDefManager *cidef = m_gamedef->cidef();
404 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
406 return InventoryItem::getBurnTime();
407 return def->furnace_burntime;
410 s16 CraftItem::getDropCount() const
413 ICraftItemDefManager *cidef = m_gamedef->cidef();
414 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
415 if(def != NULL && def->dropcount >= 0)
416 return def->dropcount;
418 return InventoryItem::getDropCount();
421 bool CraftItem::areLiquidsPointable() const
423 ICraftItemDefManager *cidef = m_gamedef->cidef();
424 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
425 return def != NULL && def->liquids_pointable;
428 bool CraftItem::dropOrPlace(ServerEnvironment *env,
429 ServerActiveObject *dropper,
430 v3f pos, bool place, s16 count)
435 bool callback_exists = false;
440 result = scriptapi_craftitem_on_place_on_ground(
442 m_subname.c_str(), dropper, pos,
446 // note: on_drop is fallback for on_place_on_ground
450 result = scriptapi_craftitem_on_drop(
452 m_subname.c_str(), dropper, pos,
458 // If the callback returned true, drop one item
460 setCount(getCount() - 1);
461 return getCount() < 1;
465 // If neither on_place_on_ground (if place==true)
466 // nor on_drop exists, call the base implementation
467 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
471 bool CraftItem::use(ServerEnvironment *env,
472 ServerActiveObject *user,
473 const PointedThing& pointed)
475 bool callback_exists = false;
478 result = scriptapi_craftitem_on_use(
480 m_subname.c_str(), user, pointed,
485 // If the callback returned true, drop one item
487 setCount(getCount() - 1);
488 return getCount() < 1;
492 // If neither on_place_on_ground (if place==true)
493 // nor on_drop exists, call the base implementation
494 return InventoryItem::use(env, user, pointed);
502 InventoryList::InventoryList(std::string name, u32 size)
510 InventoryList::~InventoryList()
512 for(u32 i=0; i<m_items.size(); i++)
519 void InventoryList::clearItems()
521 for(u32 i=0; i<m_items.size(); i++)
529 for(u32 i=0; i<m_size; i++)
531 m_items.push_back(NULL);
537 void InventoryList::serialize(std::ostream &os) const
539 //os.imbue(std::locale("C"));
541 for(u32 i=0; i<m_items.size(); i++)
543 InventoryItem *item = m_items[i];
556 os<<"EndInventoryList\n";
559 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
561 //is.imbue(std::locale("C"));
569 std::getline(is, line, '\n');
571 std::istringstream iss(line);
572 //iss.imbue(std::locale("C"));
575 std::getline(iss, name, ' ');
577 if(name == "EndInventoryList")
581 // This is a temporary backwards compatibility fix
582 else if(name == "end")
586 else if(name == "Item")
588 if(item_i > getSize() - 1)
589 throw SerializationError("too many items");
590 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
591 m_items[item_i++] = item;
593 else if(name == "Empty")
595 if(item_i > getSize() - 1)
596 throw SerializationError("too many items");
597 m_items[item_i++] = NULL;
601 throw SerializationError("Unknown inventory identifier");
606 InventoryList::InventoryList(const InventoryList &other)
609 Do this so that the items get cloned. Otherwise the pointers
610 in the array will just get copied.
615 InventoryList & InventoryList::operator = (const InventoryList &other)
617 m_name = other.m_name;
618 m_size = other.m_size;
620 for(u32 i=0; i<other.m_items.size(); i++)
622 InventoryItem *item = other.m_items[i];
625 m_items[i] = item->clone();
633 const std::string &InventoryList::getName() const
638 u32 InventoryList::getSize()
640 return m_items.size();
643 u32 InventoryList::getUsedSlots()
646 for(u32 i=0; i<m_items.size(); i++)
648 InventoryItem *item = m_items[i];
655 u32 InventoryList::getFreeSlots()
657 return getSize() - getUsedSlots();
660 const InventoryItem * InventoryList::getItem(u32 i) const
662 if(i >= m_items.size())
667 InventoryItem * InventoryList::getItem(u32 i)
669 if(i >= m_items.size())
674 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
676 if(i >= m_items.size())
679 InventoryItem *olditem = m_items[i];
680 m_items[i] = newitem;
685 void InventoryList::deleteItem(u32 i)
687 assert(i < m_items.size());
688 InventoryItem *item = changeItem(i, NULL);
693 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
699 First try to find if it could be added to some existing items
701 for(u32 i=0; i<m_items.size(); i++)
703 // Ignore empty slots
704 if(m_items[i] == NULL)
707 newitem = addItem(i, newitem);
709 return NULL; // All was eaten
713 Then try to add it to empty slots
715 for(u32 i=0; i<m_items.size(); i++)
717 // Ignore unempty slots
718 if(m_items[i] != NULL)
721 newitem = addItem(i, newitem);
723 return NULL; // All was eaten
730 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
734 if(i >= m_items.size())
739 // If it is an empty position, it's an easy job.
740 InventoryItem *to_item = getItem(i);
743 m_items[i] = newitem;
747 // If not addable, return the item
748 if(newitem->addableTo(to_item) == false)
751 // If the item fits fully in the slot, add counter and delete it
752 if(newitem->getCount() <= to_item->freeSpace())
754 to_item->add(newitem->getCount());
758 // Else the item does not fit fully. Add all that fits and return
762 u16 freespace = to_item->freeSpace();
763 to_item->add(freespace);
764 newitem->remove(freespace);
769 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
771 // If it is an empty position, it's an easy job.
772 const InventoryItem *to_item = getItem(i);
778 // If not addable, fail
779 if(newitem->addableTo(to_item) == false)
782 // If the item fits fully in the slot, pass
783 if(newitem->getCount() <= to_item->freeSpace())
791 bool InventoryList::roomForItem(const InventoryItem *item)
793 for(u32 i=0; i<m_items.size(); i++)
794 if(itemFits(i, item))
799 bool InventoryList::roomForCookedItem(const InventoryItem *item)
803 const InventoryItem *cook = item->createCookResult();
806 bool room = roomForItem(cook);
811 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
818 InventoryItem *item = getItem(i);
819 // If it is an empty position, return NULL
823 if(count >= item->getCount())
825 // Get the item by swapping NULL to its place
826 return changeItem(i, NULL);
830 InventoryItem *item2 = item->clone();
832 item2->setCount(count);
839 void InventoryList::decrementMaterials(u16 count)
841 for(u32 i=0; i<m_items.size(); i++)
843 InventoryItem *item = takeItem(i, count);
849 void InventoryList::print(std::ostream &o)
851 o<<"InventoryList:"<<std::endl;
852 for(u32 i=0; i<m_items.size(); i++)
854 InventoryItem *item = m_items[i];
868 Inventory::~Inventory()
873 void Inventory::clear()
875 for(u32 i=0; i<m_lists.size(); i++)
882 Inventory::Inventory()
886 Inventory::Inventory(const Inventory &other)
891 Inventory & Inventory::operator = (const Inventory &other)
894 for(u32 i=0; i<other.m_lists.size(); i++)
896 m_lists.push_back(new InventoryList(*other.m_lists[i]));
901 void Inventory::serialize(std::ostream &os) const
903 for(u32 i=0; i<m_lists.size(); i++)
905 InventoryList *list = m_lists[i];
906 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
910 os<<"EndInventory\n";
913 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
920 std::getline(is, line, '\n');
922 std::istringstream iss(line);
925 std::getline(iss, name, ' ');
927 if(name == "EndInventory")
931 // This is a temporary backwards compatibility fix
932 else if(name == "end")
936 else if(name == "List")
938 std::string listname;
941 std::getline(iss, listname, ' ');
944 InventoryList *list = new InventoryList(listname, listsize);
945 list->deSerialize(is, gamedef);
947 m_lists.push_back(list);
951 throw SerializationError("Unknown inventory identifier");
956 InventoryList * Inventory::addList(const std::string &name, u32 size)
958 s32 i = getListIndex(name);
961 if(m_lists[i]->getSize() != size)
964 m_lists[i] = new InventoryList(name, size);
970 m_lists.push_back(new InventoryList(name, size));
971 return m_lists.getLast();
975 InventoryList * Inventory::getList(const std::string &name)
977 s32 i = getListIndex(name);
983 bool Inventory::deleteList(const std::string &name)
985 s32 i = getListIndex(name);
993 const InventoryList * Inventory::getList(const std::string &name) const
995 s32 i = getListIndex(name);
1001 const s32 Inventory::getListIndex(const std::string &name) const
1003 for(u32 i=0; i<m_lists.size(); i++)
1005 if(m_lists[i]->getName() == name)
1015 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1018 std::getline(is, type, ' ');
1020 InventoryAction *a = NULL;
1024 a = new IMoveAction(is);
1026 else if(type == "Drop")
1028 a = new IDropAction(is);
1034 static std::string describeC(const struct InventoryContext *c)
1036 if(c->current_player == NULL)
1037 return "current_player=NULL";
1039 return std::string("current_player=") + c->current_player->getName();
1042 IMoveAction::IMoveAction(std::istream &is)
1046 std::getline(is, ts, ' ');
1049 std::getline(is, from_inv, ' ');
1051 std::getline(is, from_list, ' ');
1053 std::getline(is, ts, ' ');
1056 std::getline(is, to_inv, ' ');
1058 std::getline(is, to_list, ' ');
1060 std::getline(is, ts, ' ');
1064 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1065 ServerEnvironment *env)
1067 Inventory *inv_from = mgr->getInventory(c, from_inv);
1068 Inventory *inv_to = mgr->getInventory(c, to_inv);
1071 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1072 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1073 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1077 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1078 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1079 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1083 InventoryList *list_from = inv_from->getList(from_list);
1084 InventoryList *list_to = inv_to->getList(to_list);
1087 If a list doesn't exist or the source item doesn't exist
1090 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1091 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1092 <<", from_list=\""<<from_list<<"\""<<std::endl;
1096 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1097 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1098 <<", to_list=\""<<to_list<<"\""<<std::endl;
1101 if(list_from->getItem(from_i) == NULL)
1103 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1104 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1105 <<", from_list=\""<<from_list<<"\""
1106 <<" from_i="<<from_i<<std::endl;
1110 If the source and the destination slots are the same
1112 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1114 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1115 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1116 <<"\" i="<<from_i<<std::endl;
1120 // Take item from source list
1121 InventoryItem *item1 = NULL;
1123 item1 = list_from->changeItem(from_i, NULL);
1125 item1 = list_from->takeItem(from_i, count);
1127 // Try to add the item to destination list
1128 InventoryItem *olditem = item1;
1129 item1 = list_to->addItem(to_i, item1);
1131 // If something is returned, the item was not fully added
1134 // If olditem is returned, nothing was added.
1135 bool nothing_added = (item1 == olditem);
1137 // If something else is returned, part of the item was left unadded.
1138 // Add the other part back to the source item
1139 list_from->addItem(from_i, item1);
1141 // If olditem is returned, nothing was added.
1145 // Take item from source list
1146 item1 = list_from->changeItem(from_i, NULL);
1147 // Adding was not possible, swap the items.
1148 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1149 // Put item from destination list to the source list
1150 list_from->changeItem(from_i, item2);
1154 mgr->inventoryModified(c, from_inv);
1155 if(from_inv != to_inv)
1156 mgr->inventoryModified(c, to_inv);
1158 infostream<<"IMoveAction::apply(): moved at "
1159 <<"["<<describeC(c)<<"]"
1160 <<" from inv=\""<<from_inv<<"\""
1161 <<" list=\""<<from_list<<"\""
1163 <<" to inv=\""<<to_inv<<"\""
1164 <<" list=\""<<to_list<<"\""
1169 IDropAction::IDropAction(std::istream &is)
1173 std::getline(is, ts, ' ');
1176 std::getline(is, from_inv, ' ');
1178 std::getline(is, from_list, ' ');
1180 std::getline(is, ts, ' ');
1184 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1185 ServerEnvironment *env)
1187 if(c->current_player == NULL){
1188 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1192 // Do NOT cast directly to ServerActiveObject*, it breaks
1193 // because of multiple inheritance.
1194 ServerActiveObject *dropper =
1195 static_cast<ServerActiveObject*>(
1196 static_cast<ServerRemotePlayer*>(
1200 Inventory *inv_from = mgr->getInventory(c, from_inv);
1203 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1204 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1208 InventoryList *list_from = inv_from->getList(from_list);
1211 If a list doesn't exist or the source item doesn't exist
1214 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1215 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1216 <<", from_list=\""<<from_list<<"\""<<std::endl;
1219 InventoryItem *item = list_from->getItem(from_i);
1222 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1223 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1224 <<", from_list=\""<<from_list<<"\""
1225 <<" from_i="<<from_i<<std::endl;
1229 v3f pos = dropper->getBasePosition();
1239 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1241 list_from->deleteItem(from_i);
1243 mgr->inventoryModified(c, from_inv);
1245 infostream<<"IDropAction::apply(): dropped "
1246 <<"["<<describeC(c)<<"]"
1247 <<" from inv=\""<<from_inv<<"\""
1248 <<" list=\""<<from_list<<"\""
1254 Craft checking system
1257 bool ItemSpec::checkItem(const InventoryItem *item) const
1259 if(type == ITEM_NONE)
1261 // Has to be no item
1267 // There should be an item
1271 std::string itemname = item->getName();
1273 if(type == ITEM_MATERIAL)
1275 if(itemname != "MaterialItem")
1277 MaterialItem *mitem = (MaterialItem*)item;
1279 if(mitem->getMaterial() != num)
1282 if(mitem->getNodeName() != name)
1286 else if(type == ITEM_CRAFT)
1288 if(itemname != "CraftItem")
1290 CraftItem *mitem = (CraftItem*)item;
1291 if(mitem->getSubName() != name)
1294 else if(type == ITEM_TOOL)
1296 // Not supported yet
1299 else if(type == ITEM_MBO)
1301 // Not supported yet
1306 // Not supported yet
1312 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1314 u16 items_min_x = 100;
1315 u16 items_max_x = 100;
1316 u16 items_min_y = 100;
1317 u16 items_max_y = 100;
1318 for(u16 y=0; y<3; y++)
1319 for(u16 x=0; x<3; x++)
1321 if(items[y*3 + x] == NULL)
1323 if(items_min_x == 100 || x < items_min_x)
1325 if(items_min_y == 100 || y < items_min_y)
1327 if(items_max_x == 100 || x > items_max_x)
1329 if(items_max_y == 100 || y > items_max_y)
1332 // No items at all, just return false
1333 if(items_min_x == 100)
1336 u16 items_w = items_max_x - items_min_x + 1;
1337 u16 items_h = items_max_y - items_min_y + 1;
1339 u16 specs_min_x = 100;
1340 u16 specs_max_x = 100;
1341 u16 specs_min_y = 100;
1342 u16 specs_max_y = 100;
1343 for(u16 y=0; y<3; y++)
1344 for(u16 x=0; x<3; x++)
1346 if(specs[y*3 + x].type == ITEM_NONE)
1348 if(specs_min_x == 100 || x < specs_min_x)
1350 if(specs_min_y == 100 || y < specs_min_y)
1352 if(specs_max_x == 100 || x > specs_max_x)
1354 if(specs_max_y == 100 || y > specs_max_y)
1357 // No specs at all, just return false
1358 if(specs_min_x == 100)
1361 u16 specs_w = specs_max_x - specs_min_x + 1;
1362 u16 specs_h = specs_max_y - specs_min_y + 1;
1365 if(items_w != specs_w || items_h != specs_h)
1368 for(u16 y=0; y<specs_h; y++)
1369 for(u16 x=0; x<specs_w; x++)
1371 u16 items_x = items_min_x + x;
1372 u16 items_y = items_min_y + y;
1373 u16 specs_x = specs_min_x + x;
1374 u16 specs_y = specs_min_y + y;
1375 const InventoryItem *item = items[items_y * 3 + items_x];
1376 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1378 if(spec.checkItem(item) == false)
1385 bool checkItemCombination(const InventoryItem * const * items,
1386 const InventoryItem * const * specs)
1388 u16 items_min_x = 100;
1389 u16 items_max_x = 100;
1390 u16 items_min_y = 100;
1391 u16 items_max_y = 100;
1392 for(u16 y=0; y<3; y++)
1393 for(u16 x=0; x<3; x++)
1395 if(items[y*3 + x] == NULL)
1397 if(items_min_x == 100 || x < items_min_x)
1399 if(items_min_y == 100 || y < items_min_y)
1401 if(items_max_x == 100 || x > items_max_x)
1403 if(items_max_y == 100 || y > items_max_y)
1406 // No items at all, just return false
1407 if(items_min_x == 100)
1410 u16 items_w = items_max_x - items_min_x + 1;
1411 u16 items_h = items_max_y - items_min_y + 1;
1413 u16 specs_min_x = 100;
1414 u16 specs_max_x = 100;
1415 u16 specs_min_y = 100;
1416 u16 specs_max_y = 100;
1417 for(u16 y=0; y<3; y++)
1418 for(u16 x=0; x<3; x++)
1420 if(specs[y*3 + x] == NULL)
1422 if(specs_min_x == 100 || x < specs_min_x)
1424 if(specs_min_y == 100 || y < specs_min_y)
1426 if(specs_max_x == 100 || x > specs_max_x)
1428 if(specs_max_y == 100 || y > specs_max_y)
1431 // No specs at all, just return false
1432 if(specs_min_x == 100)
1435 u16 specs_w = specs_max_x - specs_min_x + 1;
1436 u16 specs_h = specs_max_y - specs_min_y + 1;
1439 if(items_w != specs_w || items_h != specs_h)
1442 for(u16 y=0; y<specs_h; y++)
1443 for(u16 x=0; x<specs_w; x++)
1445 u16 items_x = items_min_x + x;
1446 u16 items_y = items_min_y + y;
1447 u16 specs_x = specs_min_x + x;
1448 u16 specs_y = specs_min_y + y;
1449 const InventoryItem *item = items[items_y * 3 + items_x];
1450 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1452 if(item == NULL && spec == NULL)
1454 if(item == NULL && spec != NULL)
1456 if(item != NULL && spec == NULL)
1458 if(!spec->isSubsetOf(item))