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
40 #include "serverremoteplayer.h"
46 InventoryItem::InventoryItem(IGameDef *gamedef, u16 count):
53 InventoryItem::~InventoryItem()
57 content_t content_translate_from_19_to_internal(content_t c_from)
59 for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
61 if(trans_table_19[i][1] == c_from)
63 return trans_table_19[i][0];
69 InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef)
71 DSTACK(__FUNCTION_NAME);
73 //is.imbue(std::locale("C"));
76 std::getline(is, name, ' ');
78 if(name == "MaterialItem")
80 // u16 reads directly as a number (u8 doesn't)
85 // Convert old materials
87 material = content_translate_from_19_to_internal(material);
88 if(material > MAX_CONTENT)
89 throw SerializationError("Too large material number");
90 return new MaterialItem(gamedef, material, count);
92 else if(name == "MaterialItem2")
98 if(material > MAX_CONTENT)
99 throw SerializationError("Too large material number");
100 return new MaterialItem(gamedef, material, count);
102 else if(name == "NodeItem" || name == "MaterialItem3")
105 std::getline(is, all, '\n');
106 std::string nodename;
107 // First attempt to read inside ""
110 // If didn't skip to end, we have ""s
112 nodename = fnd.next("\"");
113 } else { // No luck, just read a word then
115 nodename = fnd.next(" ");
118 u16 count = stoi(trim(fnd.next("")));
121 return new MaterialItem(gamedef, nodename, count);
123 else if(name == "MBOItem")
125 std::string inventorystring;
126 std::getline(is, inventorystring, '|');
127 throw SerializationError("MBOItem not supported anymore");
129 else if(name == "CraftItem")
132 std::getline(is, all, '\n');
134 // First attempt to read inside ""
137 // If didn't skip to end, we have ""s
139 subname = fnd.next("\"");
140 } else { // No luck, just read a word then
142 subname = fnd.next(" ");
146 u16 count = stoi(trim(fnd.next("")));
149 return new CraftItem(gamedef, subname, count);
151 else if(name == "ToolItem")
154 std::getline(is, all, '\n');
155 std::string toolname;
156 // First attempt to read inside ""
159 // If didn't skip to end, we have ""s
161 toolname = fnd.next("\"");
162 } else { // No luck, just read a word then
164 toolname = fnd.next(" ");
168 u16 wear = stoi(trim(fnd.next("")));
169 return new ToolItem(gamedef, toolname, wear);
173 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
174 throw SerializationError("Unknown InventoryItem name");
178 InventoryItem* InventoryItem::deSerialize(const std::string &str,
181 std::istringstream is(str, std::ios_base::binary);
182 return deSerialize(is, gamedef);
185 std::string InventoryItem::getItemString() {
187 std::ostringstream os(std::ios_base::binary);
192 bool InventoryItem::dropOrPlace(ServerEnvironment *env,
193 ServerActiveObject *dropper,
194 v3f pos, bool place, s16 count)
197 Ensure that the block is loaded so that the item
198 can properly be added to the static list too
200 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
201 MapBlock *block = env->getMap().emergeBlock(blockpos, false);
204 infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: "
205 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
211 Take specified number of items,
212 but limit to getDropCount().
214 s16 dropcount = getDropCount();
215 if(count < 0 || count > dropcount)
217 if(count < 0 || count > getCount())
224 pos.Y -= BS*0.25; // let it drop a bit
226 //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
227 //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
229 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
230 // Add the object to the environment
231 env->addActiveObject(obj);
232 infostream<<"Dropped item"<<std::endl;
234 setCount(getCount() - count);
237 return getCount() < 1; // delete the item?
244 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
245 InventoryItem(gamedef, count)
248 nodename = "unknown_block";
249 m_nodename = nodename;
251 // Legacy constructor
252 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
253 InventoryItem(gamedef, count)
255 NameIdMapping legacy_nimap;
256 content_mapnode_get_name_id_mapping(&legacy_nimap);
257 std::string nodename;
258 legacy_nimap.getName(content, nodename);
260 nodename = "unknown_block";
261 m_nodename = nodename;
265 video::ITexture * MaterialItem::getImage() const
267 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
271 bool MaterialItem::isCookable() const
273 INodeDefManager *ndef = m_gamedef->ndef();
274 const ContentFeatures &f = ndef->get(m_nodename);
275 return (f.cookresult_item != "");
278 InventoryItem *MaterialItem::createCookResult() const
280 INodeDefManager *ndef = m_gamedef->ndef();
281 const ContentFeatures &f = ndef->get(m_nodename);
282 std::istringstream is(f.cookresult_item, std::ios::binary);
283 return InventoryItem::deSerialize(is, m_gamedef);
286 float MaterialItem::getCookTime() const
288 INodeDefManager *ndef = m_gamedef->ndef();
289 const ContentFeatures &f = ndef->get(m_nodename);
290 return f.furnace_cooktime;
293 float MaterialItem::getBurnTime() const
295 INodeDefManager *ndef = m_gamedef->ndef();
296 const ContentFeatures &f = ndef->get(m_nodename);
297 return f.furnace_burntime;
300 content_t MaterialItem::getMaterial() const
302 INodeDefManager *ndef = m_gamedef->ndef();
303 content_t id = CONTENT_IGNORE;
304 ndef->getId(m_nodename, id);
312 std::string ToolItem::getImageBasename() const
314 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
318 video::ITexture * ToolItem::getImage() const
320 ITextureSource *tsrc = m_gamedef->tsrc();
322 std::string basename = getImageBasename();
325 Calculate a progress value with sane amount of
328 u32 maxprogress = 30;
329 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
331 float value_f = (float)toolprogress / (float)maxprogress;
332 std::ostringstream os;
333 os<<basename<<"^[progressbar"<<value_f;
335 return tsrc->getTextureRaw(os.str());
338 video::ITexture * ToolItem::getImageRaw() const
340 ITextureSource *tsrc = m_gamedef->tsrc();
342 return tsrc->getTextureRaw(getImageBasename());
351 video::ITexture * CraftItem::getImage() const
353 ICraftItemDefManager *cidef = m_gamedef->cidef();
354 ITextureSource *tsrc = m_gamedef->tsrc();
355 std::string imagename = cidef->getImagename(m_subname);
356 return tsrc->getTextureRaw(imagename);
360 u16 CraftItem::getStackMax() const
362 ICraftItemDefManager *cidef = m_gamedef->cidef();
363 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
365 return InventoryItem::getStackMax();
366 return def->stack_max;
369 bool CraftItem::isUsable() const
371 ICraftItemDefManager *cidef = m_gamedef->cidef();
372 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
373 return def != NULL && def->usable;
376 bool CraftItem::isCookable() const
378 ICraftItemDefManager *cidef = m_gamedef->cidef();
379 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
380 return def != NULL && def->cookresult_item != "";
383 InventoryItem *CraftItem::createCookResult() const
385 ICraftItemDefManager *cidef = m_gamedef->cidef();
386 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
388 return InventoryItem::createCookResult();
389 std::istringstream is(def->cookresult_item, std::ios::binary);
390 return InventoryItem::deSerialize(is, m_gamedef);
393 float CraftItem::getCookTime() const
395 ICraftItemDefManager *cidef = m_gamedef->cidef();
396 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
398 return InventoryItem::getCookTime();
399 return def->furnace_cooktime;
402 float CraftItem::getBurnTime() const
404 ICraftItemDefManager *cidef = m_gamedef->cidef();
405 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
407 return InventoryItem::getBurnTime();
408 return def->furnace_burntime;
411 s16 CraftItem::getDropCount() const
414 ICraftItemDefManager *cidef = m_gamedef->cidef();
415 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
416 if(def != NULL && def->dropcount >= 0)
417 return def->dropcount;
419 return InventoryItem::getDropCount();
422 bool CraftItem::areLiquidsPointable() const
424 ICraftItemDefManager *cidef = m_gamedef->cidef();
425 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
426 return def != NULL && def->liquids_pointable;
429 bool CraftItem::dropOrPlace(ServerEnvironment *env,
430 ServerActiveObject *dropper,
431 v3f pos, bool place, s16 count)
436 bool callback_exists = false;
441 result = scriptapi_craftitem_on_place_on_ground(
443 m_subname.c_str(), dropper, pos,
447 // note: on_drop is fallback for on_place_on_ground
451 result = scriptapi_craftitem_on_drop(
453 m_subname.c_str(), dropper, pos,
459 // If the callback returned true, drop one item
461 setCount(getCount() - 1);
462 return getCount() < 1;
466 // If neither on_place_on_ground (if place==true)
467 // nor on_drop exists, call the base implementation
468 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
472 bool CraftItem::use(ServerEnvironment *env,
473 ServerActiveObject *user,
474 const PointedThing& pointed)
476 bool callback_exists = false;
479 result = scriptapi_craftitem_on_use(
481 m_subname.c_str(), user, pointed,
486 // If the callback returned true, drop one item
488 setCount(getCount() - 1);
489 return getCount() < 1;
493 // If neither on_place_on_ground (if place==true)
494 // nor on_drop exists, call the base implementation
495 return InventoryItem::use(env, user, pointed);
503 InventoryList::InventoryList(std::string name, u32 size)
511 InventoryList::~InventoryList()
513 for(u32 i=0; i<m_items.size(); i++)
520 void InventoryList::clearItems()
522 for(u32 i=0; i<m_items.size(); i++)
530 for(u32 i=0; i<m_size; i++)
532 m_items.push_back(NULL);
538 void InventoryList::serialize(std::ostream &os) const
540 //os.imbue(std::locale("C"));
542 for(u32 i=0; i<m_items.size(); i++)
544 InventoryItem *item = m_items[i];
557 os<<"EndInventoryList\n";
560 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
562 //is.imbue(std::locale("C"));
570 std::getline(is, line, '\n');
572 std::istringstream iss(line);
573 //iss.imbue(std::locale("C"));
576 std::getline(iss, name, ' ');
578 if(name == "EndInventoryList")
582 // This is a temporary backwards compatibility fix
583 else if(name == "end")
587 else if(name == "Item")
589 if(item_i > getSize() - 1)
590 throw SerializationError("too many items");
591 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
592 m_items[item_i++] = item;
594 else if(name == "Empty")
596 if(item_i > getSize() - 1)
597 throw SerializationError("too many items");
598 m_items[item_i++] = NULL;
602 throw SerializationError("Unknown inventory identifier");
607 InventoryList::InventoryList(const InventoryList &other)
610 Do this so that the items get cloned. Otherwise the pointers
611 in the array will just get copied.
616 InventoryList & InventoryList::operator = (const InventoryList &other)
618 m_name = other.m_name;
619 m_size = other.m_size;
621 for(u32 i=0; i<other.m_items.size(); i++)
623 InventoryItem *item = other.m_items[i];
626 m_items[i] = item->clone();
634 const std::string &InventoryList::getName() const
639 u32 InventoryList::getSize()
641 return m_items.size();
644 u32 InventoryList::getUsedSlots()
647 for(u32 i=0; i<m_items.size(); i++)
649 InventoryItem *item = m_items[i];
656 u32 InventoryList::getFreeSlots()
658 return getSize() - getUsedSlots();
661 const InventoryItem * InventoryList::getItem(u32 i) const
663 if(i >= m_items.size())
668 InventoryItem * InventoryList::getItem(u32 i)
670 if(i >= m_items.size())
675 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
677 if(i >= m_items.size())
680 InventoryItem *olditem = m_items[i];
681 m_items[i] = newitem;
686 void InventoryList::deleteItem(u32 i)
688 assert(i < m_items.size());
689 InventoryItem *item = changeItem(i, NULL);
694 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
700 First try to find if it could be added to some existing items
702 for(u32 i=0; i<m_items.size(); i++)
704 // Ignore empty slots
705 if(m_items[i] == NULL)
708 newitem = addItem(i, newitem);
710 return NULL; // All was eaten
714 Then try to add it to empty slots
716 for(u32 i=0; i<m_items.size(); i++)
718 // Ignore unempty slots
719 if(m_items[i] != NULL)
722 newitem = addItem(i, newitem);
724 return NULL; // All was eaten
731 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
735 if(i >= m_items.size())
740 // If it is an empty position, it's an easy job.
741 InventoryItem *to_item = getItem(i);
744 m_items[i] = newitem;
748 // If not addable, return the item
749 if(newitem->addableTo(to_item) == false)
752 // If the item fits fully in the slot, add counter and delete it
753 if(newitem->getCount() <= to_item->freeSpace())
755 to_item->add(newitem->getCount());
759 // Else the item does not fit fully. Add all that fits and return
763 u16 freespace = to_item->freeSpace();
764 to_item->add(freespace);
765 newitem->remove(freespace);
770 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
772 // If it is an empty position, it's an easy job.
773 const InventoryItem *to_item = getItem(i);
779 // If not addable, fail
780 if(newitem->addableTo(to_item) == false)
783 // If the item fits fully in the slot, pass
784 if(newitem->getCount() <= to_item->freeSpace())
792 bool InventoryList::roomForItem(const InventoryItem *item)
794 for(u32 i=0; i<m_items.size(); i++)
795 if(itemFits(i, item))
800 bool InventoryList::roomForCookedItem(const InventoryItem *item)
804 const InventoryItem *cook = item->createCookResult();
807 bool room = roomForItem(cook);
812 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
819 InventoryItem *item = getItem(i);
820 // If it is an empty position, return NULL
824 if(count >= item->getCount())
826 // Get the item by swapping NULL to its place
827 return changeItem(i, NULL);
831 InventoryItem *item2 = item->clone();
833 item2->setCount(count);
840 void InventoryList::decrementMaterials(u16 count)
842 for(u32 i=0; i<m_items.size(); i++)
844 InventoryItem *item = takeItem(i, count);
850 void InventoryList::print(std::ostream &o)
852 o<<"InventoryList:"<<std::endl;
853 for(u32 i=0; i<m_items.size(); i++)
855 InventoryItem *item = m_items[i];
869 Inventory::~Inventory()
874 void Inventory::clear()
876 for(u32 i=0; i<m_lists.size(); i++)
883 Inventory::Inventory()
887 Inventory::Inventory(const Inventory &other)
892 Inventory & Inventory::operator = (const Inventory &other)
895 for(u32 i=0; i<other.m_lists.size(); i++)
897 m_lists.push_back(new InventoryList(*other.m_lists[i]));
902 void Inventory::serialize(std::ostream &os) const
904 for(u32 i=0; i<m_lists.size(); i++)
906 InventoryList *list = m_lists[i];
907 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
911 os<<"EndInventory\n";
914 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
921 std::getline(is, line, '\n');
923 std::istringstream iss(line);
926 std::getline(iss, name, ' ');
928 if(name == "EndInventory")
932 // This is a temporary backwards compatibility fix
933 else if(name == "end")
937 else if(name == "List")
939 std::string listname;
942 std::getline(iss, listname, ' ');
945 InventoryList *list = new InventoryList(listname, listsize);
946 list->deSerialize(is, gamedef);
948 m_lists.push_back(list);
952 throw SerializationError("Unknown inventory identifier");
957 InventoryList * Inventory::addList(const std::string &name, u32 size)
959 s32 i = getListIndex(name);
962 if(m_lists[i]->getSize() != size)
965 m_lists[i] = new InventoryList(name, size);
971 m_lists.push_back(new InventoryList(name, size));
972 return m_lists.getLast();
976 InventoryList * Inventory::getList(const std::string &name)
978 s32 i = getListIndex(name);
984 bool Inventory::deleteList(const std::string &name)
986 s32 i = getListIndex(name);
994 const InventoryList * Inventory::getList(const std::string &name) const
996 s32 i = getListIndex(name);
1002 const s32 Inventory::getListIndex(const std::string &name) const
1004 for(u32 i=0; i<m_lists.size(); i++)
1006 if(m_lists[i]->getName() == name)
1016 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1019 std::getline(is, type, ' ');
1021 InventoryAction *a = NULL;
1025 a = new IMoveAction(is);
1027 else if(type == "Drop")
1029 a = new IDropAction(is);
1035 static std::string describeC(const struct InventoryContext *c)
1037 if(c->current_player == NULL)
1038 return "current_player=NULL";
1040 return std::string("current_player=") + c->current_player->getName();
1043 IMoveAction::IMoveAction(std::istream &is)
1047 std::getline(is, ts, ' ');
1050 std::getline(is, from_inv, ' ');
1052 std::getline(is, from_list, ' ');
1054 std::getline(is, ts, ' ');
1057 std::getline(is, to_inv, ' ');
1059 std::getline(is, to_list, ' ');
1061 std::getline(is, ts, ' ');
1065 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1066 ServerEnvironment *env)
1068 Inventory *inv_from = mgr->getInventory(c, from_inv);
1069 Inventory *inv_to = mgr->getInventory(c, to_inv);
1072 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1073 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1074 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1078 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1079 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1080 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1084 InventoryList *list_from = inv_from->getList(from_list);
1085 InventoryList *list_to = inv_to->getList(to_list);
1088 If a list doesn't exist or the source item doesn't exist
1091 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1092 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1093 <<", from_list=\""<<from_list<<"\""<<std::endl;
1097 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1098 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1099 <<", to_list=\""<<to_list<<"\""<<std::endl;
1102 if(list_from->getItem(from_i) == NULL)
1104 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1105 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1106 <<", from_list=\""<<from_list<<"\""
1107 <<" from_i="<<from_i<<std::endl;
1111 If the source and the destination slots are the same
1113 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1115 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1116 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1117 <<"\" i="<<from_i<<std::endl;
1121 // Take item from source list
1122 InventoryItem *item1 = NULL;
1124 item1 = list_from->changeItem(from_i, NULL);
1126 item1 = list_from->takeItem(from_i, count);
1128 // Try to add the item to destination list
1129 InventoryItem *olditem = item1;
1130 item1 = list_to->addItem(to_i, item1);
1132 // If something is returned, the item was not fully added
1135 // If olditem is returned, nothing was added.
1136 bool nothing_added = (item1 == olditem);
1138 // If something else is returned, part of the item was left unadded.
1139 // Add the other part back to the source item
1140 list_from->addItem(from_i, item1);
1142 // If olditem is returned, nothing was added.
1146 // Take item from source list
1147 item1 = list_from->changeItem(from_i, NULL);
1148 // Adding was not possible, swap the items.
1149 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1150 // Put item from destination list to the source list
1151 list_from->changeItem(from_i, item2);
1155 mgr->inventoryModified(c, from_inv);
1156 if(from_inv != to_inv)
1157 mgr->inventoryModified(c, to_inv);
1159 infostream<<"IMoveAction::apply(): moved at "
1160 <<"["<<describeC(c)<<"]"
1161 <<" from inv=\""<<from_inv<<"\""
1162 <<" list=\""<<from_list<<"\""
1164 <<" to inv=\""<<to_inv<<"\""
1165 <<" list=\""<<to_list<<"\""
1170 IDropAction::IDropAction(std::istream &is)
1174 std::getline(is, ts, ' ');
1177 std::getline(is, from_inv, ' ');
1179 std::getline(is, from_list, ' ');
1181 std::getline(is, ts, ' ');
1185 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1186 ServerEnvironment *env)
1188 if(c->current_player == NULL){
1189 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1193 // Do NOT cast directly to ServerActiveObject*, it breaks
1194 // because of multiple inheritance.
1195 ServerActiveObject *dropper =
1196 static_cast<ServerActiveObject*>(
1197 static_cast<ServerRemotePlayer*>(
1201 Inventory *inv_from = mgr->getInventory(c, from_inv);
1204 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1205 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1209 InventoryList *list_from = inv_from->getList(from_list);
1212 If a list doesn't exist or the source item doesn't exist
1215 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1216 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1217 <<", from_list=\""<<from_list<<"\""<<std::endl;
1220 InventoryItem *item = list_from->getItem(from_i);
1223 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1224 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1225 <<", from_list=\""<<from_list<<"\""
1226 <<" from_i="<<from_i<<std::endl;
1230 v3f pos = dropper->getBasePosition();
1240 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1242 list_from->deleteItem(from_i);
1244 mgr->inventoryModified(c, from_inv);
1246 infostream<<"IDropAction::apply(): dropped "
1247 <<"["<<describeC(c)<<"]"
1248 <<" from inv=\""<<from_inv<<"\""
1249 <<" list=\""<<from_list<<"\""
1255 Craft checking system
1258 bool ItemSpec::checkItem(const InventoryItem *item) const
1260 if(type == ITEM_NONE)
1262 // Has to be no item
1268 // There should be an item
1272 std::string itemname = item->getName();
1274 if(type == ITEM_MATERIAL)
1276 if(itemname != "MaterialItem")
1278 MaterialItem *mitem = (MaterialItem*)item;
1280 if(mitem->getMaterial() != num)
1283 if(mitem->getNodeName() != name)
1287 else if(type == ITEM_CRAFT)
1289 if(itemname != "CraftItem")
1291 CraftItem *mitem = (CraftItem*)item;
1292 if(mitem->getSubName() != name)
1295 else if(type == ITEM_TOOL)
1297 // Not supported yet
1300 else if(type == ITEM_MBO)
1302 // Not supported yet
1307 // Not supported yet
1313 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1315 u16 items_min_x = 100;
1316 u16 items_max_x = 100;
1317 u16 items_min_y = 100;
1318 u16 items_max_y = 100;
1319 for(u16 y=0; y<3; y++)
1320 for(u16 x=0; x<3; x++)
1322 if(items[y*3 + x] == NULL)
1324 if(items_min_x == 100 || x < items_min_x)
1326 if(items_min_y == 100 || y < items_min_y)
1328 if(items_max_x == 100 || x > items_max_x)
1330 if(items_max_y == 100 || y > items_max_y)
1333 // No items at all, just return false
1334 if(items_min_x == 100)
1337 u16 items_w = items_max_x - items_min_x + 1;
1338 u16 items_h = items_max_y - items_min_y + 1;
1340 u16 specs_min_x = 100;
1341 u16 specs_max_x = 100;
1342 u16 specs_min_y = 100;
1343 u16 specs_max_y = 100;
1344 for(u16 y=0; y<3; y++)
1345 for(u16 x=0; x<3; x++)
1347 if(specs[y*3 + x].type == ITEM_NONE)
1349 if(specs_min_x == 100 || x < specs_min_x)
1351 if(specs_min_y == 100 || y < specs_min_y)
1353 if(specs_max_x == 100 || x > specs_max_x)
1355 if(specs_max_y == 100 || y > specs_max_y)
1358 // No specs at all, just return false
1359 if(specs_min_x == 100)
1362 u16 specs_w = specs_max_x - specs_min_x + 1;
1363 u16 specs_h = specs_max_y - specs_min_y + 1;
1366 if(items_w != specs_w || items_h != specs_h)
1369 for(u16 y=0; y<specs_h; y++)
1370 for(u16 x=0; x<specs_w; x++)
1372 u16 items_x = items_min_x + x;
1373 u16 items_y = items_min_y + y;
1374 u16 specs_x = specs_min_x + x;
1375 u16 specs_y = specs_min_y + y;
1376 const InventoryItem *item = items[items_y * 3 + items_x];
1377 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1379 if(spec.checkItem(item) == false)
1386 bool checkItemCombination(const InventoryItem * const * items,
1387 const InventoryItem * const * specs)
1389 u16 items_min_x = 100;
1390 u16 items_max_x = 100;
1391 u16 items_min_y = 100;
1392 u16 items_max_y = 100;
1393 for(u16 y=0; y<3; y++)
1394 for(u16 x=0; x<3; x++)
1396 if(items[y*3 + x] == NULL)
1398 if(items_min_x == 100 || x < items_min_x)
1400 if(items_min_y == 100 || y < items_min_y)
1402 if(items_max_x == 100 || x > items_max_x)
1404 if(items_max_y == 100 || y > items_max_y)
1407 // No items at all, just return false
1408 if(items_min_x == 100)
1411 u16 items_w = items_max_x - items_min_x + 1;
1412 u16 items_h = items_max_y - items_min_y + 1;
1414 u16 specs_min_x = 100;
1415 u16 specs_max_x = 100;
1416 u16 specs_min_y = 100;
1417 u16 specs_max_y = 100;
1418 for(u16 y=0; y<3; y++)
1419 for(u16 x=0; x<3; x++)
1421 if(specs[y*3 + x] == NULL)
1423 if(specs_min_x == 100 || x < specs_min_x)
1425 if(specs_min_y == 100 || y < specs_min_y)
1427 if(specs_max_x == 100 || x > specs_max_x)
1429 if(specs_max_y == 100 || y > specs_max_y)
1432 // No specs at all, just return false
1433 if(specs_min_x == 100)
1436 u16 specs_w = specs_max_x - specs_min_x + 1;
1437 u16 specs_h = specs_max_y - specs_min_y + 1;
1440 if(items_w != specs_w || items_h != specs_h)
1443 for(u16 y=0; y<specs_h; y++)
1444 for(u16 x=0; x<specs_w; x++)
1446 u16 items_x = items_min_x + x;
1447 u16 items_y = items_min_y + y;
1448 u16 specs_x = specs_min_x + x;
1449 u16 specs_y = specs_min_y + y;
1450 const InventoryItem *item = items[items_y * 3 + items_x];
1451 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1453 if(item == NULL && spec == NULL)
1455 if(item == NULL && spec != NULL)
1457 if(item != NULL && spec == NULL)
1459 if(!spec->isSubsetOf(item))