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 == "node" || 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 == "craft" || 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 == "tool" || 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());
346 bool ToolItem::isKnown() const
348 IToolDefManager *tdef = m_gamedef->tdef();
349 const ToolDefinition *def = tdef->getToolDefinition(m_toolname);
350 return (def != NULL);
358 video::ITexture * CraftItem::getImage() const
360 ICraftItemDefManager *cidef = m_gamedef->cidef();
361 ITextureSource *tsrc = m_gamedef->tsrc();
362 std::string imagename = cidef->getImagename(m_subname);
363 return tsrc->getTextureRaw(imagename);
367 bool CraftItem::isKnown() const
369 ICraftItemDefManager *cidef = m_gamedef->cidef();
370 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
371 return (def != NULL);
374 u16 CraftItem::getStackMax() const
376 ICraftItemDefManager *cidef = m_gamedef->cidef();
377 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
379 return InventoryItem::getStackMax();
380 return def->stack_max;
383 bool CraftItem::isUsable() const
385 ICraftItemDefManager *cidef = m_gamedef->cidef();
386 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
387 return def != NULL && def->usable;
390 bool CraftItem::isCookable() const
392 ICraftItemDefManager *cidef = m_gamedef->cidef();
393 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
394 return def != NULL && def->cookresult_item != "";
397 InventoryItem *CraftItem::createCookResult() const
399 ICraftItemDefManager *cidef = m_gamedef->cidef();
400 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
402 return InventoryItem::createCookResult();
403 std::istringstream is(def->cookresult_item, std::ios::binary);
404 return InventoryItem::deSerialize(is, m_gamedef);
407 float CraftItem::getCookTime() const
409 ICraftItemDefManager *cidef = m_gamedef->cidef();
410 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
412 return InventoryItem::getCookTime();
413 return def->furnace_cooktime;
416 float CraftItem::getBurnTime() const
418 ICraftItemDefManager *cidef = m_gamedef->cidef();
419 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
421 return InventoryItem::getBurnTime();
422 return def->furnace_burntime;
425 s16 CraftItem::getDropCount() const
428 ICraftItemDefManager *cidef = m_gamedef->cidef();
429 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
430 if(def != NULL && def->dropcount >= 0)
431 return def->dropcount;
433 return InventoryItem::getDropCount();
436 bool CraftItem::areLiquidsPointable() const
438 ICraftItemDefManager *cidef = m_gamedef->cidef();
439 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
440 return def != NULL && def->liquids_pointable;
443 bool CraftItem::dropOrPlace(ServerEnvironment *env,
444 ServerActiveObject *dropper,
445 v3f pos, bool place, s16 count)
450 bool callback_exists = false;
455 result = scriptapi_craftitem_on_place_on_ground(
457 m_subname.c_str(), dropper, pos,
461 // note: on_drop is fallback for on_place_on_ground
465 result = scriptapi_craftitem_on_drop(
467 m_subname.c_str(), dropper, pos,
473 // If the callback returned true, drop one item
475 setCount(getCount() - 1);
476 return getCount() < 1;
480 // If neither on_place_on_ground (if place==true)
481 // nor on_drop exists, call the base implementation
482 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
486 bool CraftItem::use(ServerEnvironment *env,
487 ServerActiveObject *user,
488 const PointedThing& pointed)
490 bool callback_exists = false;
493 result = scriptapi_craftitem_on_use(
495 m_subname.c_str(), user, pointed,
500 // If the callback returned true, drop one item
502 setCount(getCount() - 1);
503 return getCount() < 1;
507 // If neither on_place_on_ground (if place==true)
508 // nor on_drop exists, call the base implementation
509 return InventoryItem::use(env, user, pointed);
517 InventoryList::InventoryList(std::string name, u32 size)
525 InventoryList::~InventoryList()
527 for(u32 i=0; i<m_items.size(); i++)
534 void InventoryList::clearItems()
536 for(u32 i=0; i<m_items.size(); i++)
544 for(u32 i=0; i<m_size; i++)
546 m_items.push_back(NULL);
552 void InventoryList::serialize(std::ostream &os) const
554 //os.imbue(std::locale("C"));
556 for(u32 i=0; i<m_items.size(); i++)
558 InventoryItem *item = m_items[i];
571 os<<"EndInventoryList\n";
574 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
576 //is.imbue(std::locale("C"));
584 std::getline(is, line, '\n');
586 std::istringstream iss(line);
587 //iss.imbue(std::locale("C"));
590 std::getline(iss, name, ' ');
592 if(name == "EndInventoryList")
596 // This is a temporary backwards compatibility fix
597 else if(name == "end")
601 else if(name == "Item")
603 if(item_i > getSize() - 1)
604 throw SerializationError("too many items");
605 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
606 m_items[item_i++] = item;
608 else if(name == "Empty")
610 if(item_i > getSize() - 1)
611 throw SerializationError("too many items");
612 m_items[item_i++] = NULL;
616 throw SerializationError("Unknown inventory identifier");
621 InventoryList::InventoryList(const InventoryList &other)
624 Do this so that the items get cloned. Otherwise the pointers
625 in the array will just get copied.
630 InventoryList & InventoryList::operator = (const InventoryList &other)
632 m_name = other.m_name;
633 m_size = other.m_size;
635 for(u32 i=0; i<other.m_items.size(); i++)
637 InventoryItem *item = other.m_items[i];
640 m_items[i] = item->clone();
648 const std::string &InventoryList::getName() const
653 u32 InventoryList::getSize()
655 return m_items.size();
658 u32 InventoryList::getUsedSlots()
661 for(u32 i=0; i<m_items.size(); i++)
663 InventoryItem *item = m_items[i];
670 u32 InventoryList::getFreeSlots()
672 return getSize() - getUsedSlots();
675 const InventoryItem * InventoryList::getItem(u32 i) const
677 if(i >= m_items.size())
682 InventoryItem * InventoryList::getItem(u32 i)
684 if(i >= m_items.size())
689 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
691 if(i >= m_items.size())
694 InventoryItem *olditem = m_items[i];
695 m_items[i] = newitem;
700 void InventoryList::deleteItem(u32 i)
702 assert(i < m_items.size());
703 InventoryItem *item = changeItem(i, NULL);
708 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
714 First try to find if it could be added to some existing items
716 for(u32 i=0; i<m_items.size(); i++)
718 // Ignore empty slots
719 if(m_items[i] == NULL)
722 newitem = addItem(i, newitem);
724 return NULL; // All was eaten
728 Then try to add it to empty slots
730 for(u32 i=0; i<m_items.size(); i++)
732 // Ignore unempty slots
733 if(m_items[i] != NULL)
736 newitem = addItem(i, newitem);
738 return NULL; // All was eaten
745 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
749 if(i >= m_items.size())
754 // If it is an empty position, it's an easy job.
755 InventoryItem *to_item = getItem(i);
758 m_items[i] = newitem;
762 // If not addable, return the item
763 if(newitem->addableTo(to_item) == false)
766 // If the item fits fully in the slot, add counter and delete it
767 if(newitem->getCount() <= to_item->freeSpace())
769 to_item->add(newitem->getCount());
773 // Else the item does not fit fully. Add all that fits and return
777 u16 freespace = to_item->freeSpace();
778 to_item->add(freespace);
779 newitem->remove(freespace);
784 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
786 // If it is an empty position, it's an easy job.
787 const InventoryItem *to_item = getItem(i);
793 // If not addable, fail
794 if(newitem->addableTo(to_item) == false)
797 // If the item fits fully in the slot, pass
798 if(newitem->getCount() <= to_item->freeSpace())
806 bool InventoryList::roomForItem(const InventoryItem *item)
808 for(u32 i=0; i<m_items.size(); i++)
809 if(itemFits(i, item))
814 bool InventoryList::roomForCookedItem(const InventoryItem *item)
818 const InventoryItem *cook = item->createCookResult();
821 bool room = roomForItem(cook);
826 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
833 InventoryItem *item = getItem(i);
834 // If it is an empty position, return NULL
838 if(count >= item->getCount())
840 // Get the item by swapping NULL to its place
841 return changeItem(i, NULL);
845 InventoryItem *item2 = item->clone();
847 item2->setCount(count);
854 void InventoryList::decrementMaterials(u16 count)
856 for(u32 i=0; i<m_items.size(); i++)
858 InventoryItem *item = takeItem(i, count);
864 void InventoryList::print(std::ostream &o)
866 o<<"InventoryList:"<<std::endl;
867 for(u32 i=0; i<m_items.size(); i++)
869 InventoryItem *item = m_items[i];
883 Inventory::~Inventory()
888 void Inventory::clear()
890 for(u32 i=0; i<m_lists.size(); i++)
897 Inventory::Inventory()
901 Inventory::Inventory(const Inventory &other)
906 Inventory & Inventory::operator = (const Inventory &other)
909 for(u32 i=0; i<other.m_lists.size(); i++)
911 m_lists.push_back(new InventoryList(*other.m_lists[i]));
916 void Inventory::serialize(std::ostream &os) const
918 for(u32 i=0; i<m_lists.size(); i++)
920 InventoryList *list = m_lists[i];
921 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
925 os<<"EndInventory\n";
928 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
935 std::getline(is, line, '\n');
937 std::istringstream iss(line);
940 std::getline(iss, name, ' ');
942 if(name == "EndInventory")
946 // This is a temporary backwards compatibility fix
947 else if(name == "end")
951 else if(name == "List")
953 std::string listname;
956 std::getline(iss, listname, ' ');
959 InventoryList *list = new InventoryList(listname, listsize);
960 list->deSerialize(is, gamedef);
962 m_lists.push_back(list);
966 throw SerializationError("Unknown inventory identifier");
971 InventoryList * Inventory::addList(const std::string &name, u32 size)
973 s32 i = getListIndex(name);
976 if(m_lists[i]->getSize() != size)
979 m_lists[i] = new InventoryList(name, size);
985 m_lists.push_back(new InventoryList(name, size));
986 return m_lists.getLast();
990 InventoryList * Inventory::getList(const std::string &name)
992 s32 i = getListIndex(name);
998 bool Inventory::deleteList(const std::string &name)
1000 s32 i = getListIndex(name);
1008 const InventoryList * Inventory::getList(const std::string &name) const
1010 s32 i = getListIndex(name);
1016 const s32 Inventory::getListIndex(const std::string &name) const
1018 for(u32 i=0; i<m_lists.size(); i++)
1020 if(m_lists[i]->getName() == name)
1030 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1033 std::getline(is, type, ' ');
1035 InventoryAction *a = NULL;
1039 a = new IMoveAction(is);
1041 else if(type == "Drop")
1043 a = new IDropAction(is);
1049 static std::string describeC(const struct InventoryContext *c)
1051 if(c->current_player == NULL)
1052 return "current_player=NULL";
1054 return std::string("current_player=") + c->current_player->getName();
1057 IMoveAction::IMoveAction(std::istream &is)
1061 std::getline(is, ts, ' ');
1064 std::getline(is, from_inv, ' ');
1066 std::getline(is, from_list, ' ');
1068 std::getline(is, ts, ' ');
1071 std::getline(is, to_inv, ' ');
1073 std::getline(is, to_list, ' ');
1075 std::getline(is, ts, ' ');
1079 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1080 ServerEnvironment *env)
1082 Inventory *inv_from = mgr->getInventory(c, from_inv);
1083 Inventory *inv_to = mgr->getInventory(c, to_inv);
1086 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1087 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1088 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1092 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1093 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1094 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1098 InventoryList *list_from = inv_from->getList(from_list);
1099 InventoryList *list_to = inv_to->getList(to_list);
1102 If a list doesn't exist or the source item doesn't exist
1105 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1106 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1107 <<", from_list=\""<<from_list<<"\""<<std::endl;
1111 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1112 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1113 <<", to_list=\""<<to_list<<"\""<<std::endl;
1116 if(list_from->getItem(from_i) == NULL)
1118 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1119 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1120 <<", from_list=\""<<from_list<<"\""
1121 <<" from_i="<<from_i<<std::endl;
1125 If the source and the destination slots are the same
1127 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1129 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1130 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1131 <<"\" i="<<from_i<<std::endl;
1135 // Take item from source list
1136 InventoryItem *item1 = NULL;
1138 item1 = list_from->changeItem(from_i, NULL);
1140 item1 = list_from->takeItem(from_i, count);
1142 // Try to add the item to destination list
1143 InventoryItem *olditem = item1;
1144 item1 = list_to->addItem(to_i, item1);
1146 // If something is returned, the item was not fully added
1149 // If olditem is returned, nothing was added.
1150 bool nothing_added = (item1 == olditem);
1152 // If something else is returned, part of the item was left unadded.
1153 // Add the other part back to the source item
1154 list_from->addItem(from_i, item1);
1156 // If olditem is returned, nothing was added.
1160 // Take item from source list
1161 item1 = list_from->changeItem(from_i, NULL);
1162 // Adding was not possible, swap the items.
1163 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1164 // Put item from destination list to the source list
1165 list_from->changeItem(from_i, item2);
1169 mgr->inventoryModified(c, from_inv);
1170 if(from_inv != to_inv)
1171 mgr->inventoryModified(c, to_inv);
1173 infostream<<"IMoveAction::apply(): moved at "
1174 <<"["<<describeC(c)<<"]"
1175 <<" from inv=\""<<from_inv<<"\""
1176 <<" list=\""<<from_list<<"\""
1178 <<" to inv=\""<<to_inv<<"\""
1179 <<" list=\""<<to_list<<"\""
1184 IDropAction::IDropAction(std::istream &is)
1188 std::getline(is, ts, ' ');
1191 std::getline(is, from_inv, ' ');
1193 std::getline(is, from_list, ' ');
1195 std::getline(is, ts, ' ');
1199 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1200 ServerEnvironment *env)
1202 if(c->current_player == NULL){
1203 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1207 // Do NOT cast directly to ServerActiveObject*, it breaks
1208 // because of multiple inheritance.
1209 ServerActiveObject *dropper =
1210 static_cast<ServerActiveObject*>(
1211 static_cast<ServerRemotePlayer*>(
1215 Inventory *inv_from = mgr->getInventory(c, from_inv);
1218 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1219 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1223 InventoryList *list_from = inv_from->getList(from_list);
1226 If a list doesn't exist or the source item doesn't exist
1229 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1230 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1231 <<", from_list=\""<<from_list<<"\""<<std::endl;
1234 InventoryItem *item = list_from->getItem(from_i);
1237 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1238 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1239 <<", from_list=\""<<from_list<<"\""
1240 <<" from_i="<<from_i<<std::endl;
1244 v3f pos = dropper->getBasePosition();
1254 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1256 list_from->deleteItem(from_i);
1258 mgr->inventoryModified(c, from_inv);
1260 infostream<<"IDropAction::apply(): dropped "
1261 <<"["<<describeC(c)<<"]"
1262 <<" from inv=\""<<from_inv<<"\""
1263 <<" list=\""<<from_list<<"\""
1269 Craft checking system
1272 bool ItemSpec::checkItem(const InventoryItem *item) const
1274 if(type == ITEM_NONE)
1276 // Has to be no item
1282 // There should be an item
1286 std::string itemname = item->getName();
1288 if(type == ITEM_MATERIAL)
1290 if(itemname != "MaterialItem")
1292 MaterialItem *mitem = (MaterialItem*)item;
1294 if(mitem->getMaterial() != num)
1297 if(mitem->getNodeName() != name)
1301 else if(type == ITEM_CRAFT)
1303 if(itemname != "CraftItem")
1305 CraftItem *mitem = (CraftItem*)item;
1306 if(mitem->getSubName() != name)
1309 else if(type == ITEM_TOOL)
1311 // Not supported yet
1314 else if(type == ITEM_MBO)
1316 // Not supported yet
1321 // Not supported yet
1327 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1329 u16 items_min_x = 100;
1330 u16 items_max_x = 100;
1331 u16 items_min_y = 100;
1332 u16 items_max_y = 100;
1333 for(u16 y=0; y<3; y++)
1334 for(u16 x=0; x<3; x++)
1336 if(items[y*3 + x] == NULL)
1338 if(items_min_x == 100 || x < items_min_x)
1340 if(items_min_y == 100 || y < items_min_y)
1342 if(items_max_x == 100 || x > items_max_x)
1344 if(items_max_y == 100 || y > items_max_y)
1347 // No items at all, just return false
1348 if(items_min_x == 100)
1351 u16 items_w = items_max_x - items_min_x + 1;
1352 u16 items_h = items_max_y - items_min_y + 1;
1354 u16 specs_min_x = 100;
1355 u16 specs_max_x = 100;
1356 u16 specs_min_y = 100;
1357 u16 specs_max_y = 100;
1358 for(u16 y=0; y<3; y++)
1359 for(u16 x=0; x<3; x++)
1361 if(specs[y*3 + x].type == ITEM_NONE)
1363 if(specs_min_x == 100 || x < specs_min_x)
1365 if(specs_min_y == 100 || y < specs_min_y)
1367 if(specs_max_x == 100 || x > specs_max_x)
1369 if(specs_max_y == 100 || y > specs_max_y)
1372 // No specs at all, just return false
1373 if(specs_min_x == 100)
1376 u16 specs_w = specs_max_x - specs_min_x + 1;
1377 u16 specs_h = specs_max_y - specs_min_y + 1;
1380 if(items_w != specs_w || items_h != specs_h)
1383 for(u16 y=0; y<specs_h; y++)
1384 for(u16 x=0; x<specs_w; x++)
1386 u16 items_x = items_min_x + x;
1387 u16 items_y = items_min_y + y;
1388 u16 specs_x = specs_min_x + x;
1389 u16 specs_y = specs_min_y + y;
1390 const InventoryItem *item = items[items_y * 3 + items_x];
1391 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1393 if(spec.checkItem(item) == false)
1400 bool checkItemCombination(const InventoryItem * const * items,
1401 const InventoryItem * const * specs)
1403 u16 items_min_x = 100;
1404 u16 items_max_x = 100;
1405 u16 items_min_y = 100;
1406 u16 items_max_y = 100;
1407 for(u16 y=0; y<3; y++)
1408 for(u16 x=0; x<3; x++)
1410 if(items[y*3 + x] == NULL)
1412 if(items_min_x == 100 || x < items_min_x)
1414 if(items_min_y == 100 || y < items_min_y)
1416 if(items_max_x == 100 || x > items_max_x)
1418 if(items_max_y == 100 || y > items_max_y)
1421 // No items at all, just return false
1422 if(items_min_x == 100)
1425 u16 items_w = items_max_x - items_min_x + 1;
1426 u16 items_h = items_max_y - items_min_y + 1;
1428 u16 specs_min_x = 100;
1429 u16 specs_max_x = 100;
1430 u16 specs_min_y = 100;
1431 u16 specs_max_y = 100;
1432 for(u16 y=0; y<3; y++)
1433 for(u16 x=0; x<3; x++)
1435 if(specs[y*3 + x] == NULL)
1437 if(specs_min_x == 100 || x < specs_min_x)
1439 if(specs_min_y == 100 || y < specs_min_y)
1441 if(specs_max_x == 100 || x > specs_max_x)
1443 if(specs_max_y == 100 || y > specs_max_y)
1446 // No specs at all, just return false
1447 if(specs_min_x == 100)
1450 u16 specs_w = specs_max_x - specs_min_x + 1;
1451 u16 specs_h = specs_max_y - specs_min_y + 1;
1454 if(items_w != specs_w || items_h != specs_h)
1457 for(u16 y=0; y<specs_h; y++)
1458 for(u16 x=0; x<specs_w; x++)
1460 u16 items_x = items_min_x + x;
1461 u16 items_y = items_min_y + y;
1462 u16 specs_x = specs_min_x + x;
1463 u16 specs_y = specs_min_y + y;
1464 const InventoryItem *item = items[items_y * 3 + items_x];
1465 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1467 if(item == NULL && spec == NULL)
1469 if(item == NULL && spec != NULL)
1471 if(item != NULL && spec == NULL)
1473 if(!spec->isSubsetOf(item))