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";
250 // Convert directly to the correct name through aliases
251 m_nodename = gamedef->ndef()->getAlias(nodename);
253 // Legacy constructor
254 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
255 InventoryItem(gamedef, count)
257 NameIdMapping legacy_nimap;
258 content_mapnode_get_name_id_mapping(&legacy_nimap);
259 std::string nodename;
260 legacy_nimap.getName(content, nodename);
262 nodename = "unknown_block";
263 m_nodename = nodename;
267 video::ITexture * MaterialItem::getImage() const
269 return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
273 bool MaterialItem::isCookable() const
275 INodeDefManager *ndef = m_gamedef->ndef();
276 const ContentFeatures &f = ndef->get(m_nodename);
277 return (f.cookresult_item != "");
280 InventoryItem *MaterialItem::createCookResult() const
282 INodeDefManager *ndef = m_gamedef->ndef();
283 const ContentFeatures &f = ndef->get(m_nodename);
284 std::istringstream is(f.cookresult_item, std::ios::binary);
285 return InventoryItem::deSerialize(is, m_gamedef);
288 float MaterialItem::getCookTime() const
290 INodeDefManager *ndef = m_gamedef->ndef();
291 const ContentFeatures &f = ndef->get(m_nodename);
292 return f.furnace_cooktime;
295 float MaterialItem::getBurnTime() const
297 INodeDefManager *ndef = m_gamedef->ndef();
298 const ContentFeatures &f = ndef->get(m_nodename);
299 return f.furnace_burntime;
302 content_t MaterialItem::getMaterial() const
304 INodeDefManager *ndef = m_gamedef->ndef();
305 content_t id = CONTENT_IGNORE;
306 ndef->getId(m_nodename, id);
314 ToolItem::ToolItem(IGameDef *gamedef, std::string toolname, u16 wear):
315 InventoryItem(gamedef, 1)
317 // Convert directly to the correct name through aliases
318 m_toolname = gamedef->tdef()->getAlias(toolname);
323 std::string ToolItem::getImageBasename() const
325 return m_gamedef->getToolDefManager()->getImagename(m_toolname);
329 video::ITexture * ToolItem::getImage() const
331 ITextureSource *tsrc = m_gamedef->tsrc();
333 std::string basename = getImageBasename();
336 Calculate a progress value with sane amount of
339 u32 maxprogress = 30;
340 u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
342 float value_f = (float)toolprogress / (float)maxprogress;
343 std::ostringstream os;
344 os<<basename<<"^[progressbar"<<value_f;
346 return tsrc->getTextureRaw(os.str());
349 video::ITexture * ToolItem::getImageRaw() const
351 ITextureSource *tsrc = m_gamedef->tsrc();
353 return tsrc->getTextureRaw(getImageBasename());
357 bool ToolItem::isKnown() const
359 IToolDefManager *tdef = m_gamedef->tdef();
360 const ToolDefinition *def = tdef->getToolDefinition(m_toolname);
361 return (def != NULL);
368 CraftItem::CraftItem(IGameDef *gamedef, std::string subname, u16 count):
369 InventoryItem(gamedef, count)
371 // Convert directly to the correct name through aliases
372 m_subname = gamedef->cidef()->getAlias(subname);
376 video::ITexture * CraftItem::getImage() const
378 ICraftItemDefManager *cidef = m_gamedef->cidef();
379 ITextureSource *tsrc = m_gamedef->tsrc();
380 std::string imagename = cidef->getImagename(m_subname);
381 return tsrc->getTextureRaw(imagename);
385 bool CraftItem::isKnown() const
387 ICraftItemDefManager *cidef = m_gamedef->cidef();
388 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
389 return (def != NULL);
392 u16 CraftItem::getStackMax() const
394 ICraftItemDefManager *cidef = m_gamedef->cidef();
395 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
397 return InventoryItem::getStackMax();
398 return def->stack_max;
401 bool CraftItem::isUsable() const
403 ICraftItemDefManager *cidef = m_gamedef->cidef();
404 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
405 return def != NULL && def->usable;
408 bool CraftItem::isCookable() const
410 ICraftItemDefManager *cidef = m_gamedef->cidef();
411 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
412 return def != NULL && def->cookresult_item != "";
415 InventoryItem *CraftItem::createCookResult() const
417 ICraftItemDefManager *cidef = m_gamedef->cidef();
418 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
420 return InventoryItem::createCookResult();
421 std::istringstream is(def->cookresult_item, std::ios::binary);
422 return InventoryItem::deSerialize(is, m_gamedef);
425 float CraftItem::getCookTime() const
427 ICraftItemDefManager *cidef = m_gamedef->cidef();
428 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
430 return InventoryItem::getCookTime();
431 return def->furnace_cooktime;
434 float CraftItem::getBurnTime() const
436 ICraftItemDefManager *cidef = m_gamedef->cidef();
437 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
439 return InventoryItem::getBurnTime();
440 return def->furnace_burntime;
443 s16 CraftItem::getDropCount() const
446 ICraftItemDefManager *cidef = m_gamedef->cidef();
447 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
448 if(def != NULL && def->dropcount >= 0)
449 return def->dropcount;
451 return InventoryItem::getDropCount();
454 bool CraftItem::areLiquidsPointable() const
456 ICraftItemDefManager *cidef = m_gamedef->cidef();
457 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
458 return def != NULL && def->liquids_pointable;
461 bool CraftItem::dropOrPlace(ServerEnvironment *env,
462 ServerActiveObject *dropper,
463 v3f pos, bool place, s16 count)
468 bool callback_exists = false;
473 result = scriptapi_craftitem_on_place_on_ground(
475 m_subname.c_str(), dropper, pos,
479 // note: on_drop is fallback for on_place_on_ground
483 result = scriptapi_craftitem_on_drop(
485 m_subname.c_str(), dropper, pos,
491 // If the callback returned true, drop one item
493 setCount(getCount() - 1);
494 return getCount() < 1;
498 // If neither on_place_on_ground (if place==true)
499 // nor on_drop exists, call the base implementation
500 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
504 bool CraftItem::use(ServerEnvironment *env,
505 ServerActiveObject *user,
506 const PointedThing& pointed)
508 bool callback_exists = false;
511 result = scriptapi_craftitem_on_use(
513 m_subname.c_str(), user, pointed,
518 // If the callback returned true, drop one item
520 setCount(getCount() - 1);
521 return getCount() < 1;
525 // If neither on_place_on_ground (if place==true)
526 // nor on_drop exists, call the base implementation
527 return InventoryItem::use(env, user, pointed);
535 InventoryList::InventoryList(std::string name, u32 size)
543 InventoryList::~InventoryList()
545 for(u32 i=0; i<m_items.size(); i++)
552 void InventoryList::clearItems()
554 for(u32 i=0; i<m_items.size(); i++)
562 for(u32 i=0; i<m_size; i++)
564 m_items.push_back(NULL);
570 void InventoryList::serialize(std::ostream &os) const
572 //os.imbue(std::locale("C"));
574 for(u32 i=0; i<m_items.size(); i++)
576 InventoryItem *item = m_items[i];
589 os<<"EndInventoryList\n";
592 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
594 //is.imbue(std::locale("C"));
602 std::getline(is, line, '\n');
604 std::istringstream iss(line);
605 //iss.imbue(std::locale("C"));
608 std::getline(iss, name, ' ');
610 if(name == "EndInventoryList")
614 // This is a temporary backwards compatibility fix
615 else if(name == "end")
619 else if(name == "Item")
621 if(item_i > getSize() - 1)
622 throw SerializationError("too many items");
623 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
624 m_items[item_i++] = item;
626 else if(name == "Empty")
628 if(item_i > getSize() - 1)
629 throw SerializationError("too many items");
630 m_items[item_i++] = NULL;
634 throw SerializationError("Unknown inventory identifier");
639 InventoryList::InventoryList(const InventoryList &other)
642 Do this so that the items get cloned. Otherwise the pointers
643 in the array will just get copied.
648 InventoryList & InventoryList::operator = (const InventoryList &other)
650 m_name = other.m_name;
651 m_size = other.m_size;
653 for(u32 i=0; i<other.m_items.size(); i++)
655 InventoryItem *item = other.m_items[i];
658 m_items[i] = item->clone();
666 const std::string &InventoryList::getName() const
671 u32 InventoryList::getSize()
673 return m_items.size();
676 u32 InventoryList::getUsedSlots()
679 for(u32 i=0; i<m_items.size(); i++)
681 InventoryItem *item = m_items[i];
688 u32 InventoryList::getFreeSlots()
690 return getSize() - getUsedSlots();
693 const InventoryItem * InventoryList::getItem(u32 i) const
695 if(i >= m_items.size())
700 InventoryItem * InventoryList::getItem(u32 i)
702 if(i >= m_items.size())
707 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
709 if(i >= m_items.size())
712 InventoryItem *olditem = m_items[i];
713 m_items[i] = newitem;
718 void InventoryList::deleteItem(u32 i)
720 assert(i < m_items.size());
721 InventoryItem *item = changeItem(i, NULL);
726 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
732 First try to find if it could be added to some existing items
734 for(u32 i=0; i<m_items.size(); i++)
736 // Ignore empty slots
737 if(m_items[i] == NULL)
740 newitem = addItem(i, newitem);
742 return NULL; // All was eaten
746 Then try to add it to empty slots
748 for(u32 i=0; i<m_items.size(); i++)
750 // Ignore unempty slots
751 if(m_items[i] != NULL)
754 newitem = addItem(i, newitem);
756 return NULL; // All was eaten
763 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
767 if(i >= m_items.size())
772 // If it is an empty position, it's an easy job.
773 InventoryItem *to_item = getItem(i);
776 m_items[i] = newitem;
780 // If not addable, return the item
781 if(newitem->addableTo(to_item) == false)
784 // If the item fits fully in the slot, add counter and delete it
785 if(newitem->getCount() <= to_item->freeSpace())
787 to_item->add(newitem->getCount());
791 // Else the item does not fit fully. Add all that fits and return
795 u16 freespace = to_item->freeSpace();
796 to_item->add(freespace);
797 newitem->remove(freespace);
802 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
804 // If it is an empty position, it's an easy job.
805 const InventoryItem *to_item = getItem(i);
811 // If not addable, fail
812 if(newitem->addableTo(to_item) == false)
815 // If the item fits fully in the slot, pass
816 if(newitem->getCount() <= to_item->freeSpace())
824 bool InventoryList::roomForItem(const InventoryItem *item)
826 for(u32 i=0; i<m_items.size(); i++)
827 if(itemFits(i, item))
832 bool InventoryList::roomForCookedItem(const InventoryItem *item)
836 const InventoryItem *cook = item->createCookResult();
839 bool room = roomForItem(cook);
844 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
851 InventoryItem *item = getItem(i);
852 // If it is an empty position, return NULL
856 if(count >= item->getCount())
858 // Get the item by swapping NULL to its place
859 return changeItem(i, NULL);
863 InventoryItem *item2 = item->clone();
865 item2->setCount(count);
872 void InventoryList::decrementMaterials(u16 count)
874 for(u32 i=0; i<m_items.size(); i++)
876 InventoryItem *item = takeItem(i, count);
882 void InventoryList::print(std::ostream &o)
884 o<<"InventoryList:"<<std::endl;
885 for(u32 i=0; i<m_items.size(); i++)
887 InventoryItem *item = m_items[i];
901 Inventory::~Inventory()
906 void Inventory::clear()
908 for(u32 i=0; i<m_lists.size(); i++)
915 Inventory::Inventory()
919 Inventory::Inventory(const Inventory &other)
924 Inventory & Inventory::operator = (const Inventory &other)
927 for(u32 i=0; i<other.m_lists.size(); i++)
929 m_lists.push_back(new InventoryList(*other.m_lists[i]));
934 void Inventory::serialize(std::ostream &os) const
936 for(u32 i=0; i<m_lists.size(); i++)
938 InventoryList *list = m_lists[i];
939 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
943 os<<"EndInventory\n";
946 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
953 std::getline(is, line, '\n');
955 std::istringstream iss(line);
958 std::getline(iss, name, ' ');
960 if(name == "EndInventory")
964 // This is a temporary backwards compatibility fix
965 else if(name == "end")
969 else if(name == "List")
971 std::string listname;
974 std::getline(iss, listname, ' ');
977 InventoryList *list = new InventoryList(listname, listsize);
978 list->deSerialize(is, gamedef);
980 m_lists.push_back(list);
984 throw SerializationError("Unknown inventory identifier");
989 InventoryList * Inventory::addList(const std::string &name, u32 size)
991 s32 i = getListIndex(name);
994 if(m_lists[i]->getSize() != size)
997 m_lists[i] = new InventoryList(name, size);
1003 m_lists.push_back(new InventoryList(name, size));
1004 return m_lists.getLast();
1008 InventoryList * Inventory::getList(const std::string &name)
1010 s32 i = getListIndex(name);
1016 bool Inventory::deleteList(const std::string &name)
1018 s32 i = getListIndex(name);
1026 const InventoryList * Inventory::getList(const std::string &name) const
1028 s32 i = getListIndex(name);
1034 const s32 Inventory::getListIndex(const std::string &name) const
1036 for(u32 i=0; i<m_lists.size(); i++)
1038 if(m_lists[i]->getName() == name)
1048 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1051 std::getline(is, type, ' ');
1053 InventoryAction *a = NULL;
1057 a = new IMoveAction(is);
1059 else if(type == "Drop")
1061 a = new IDropAction(is);
1067 static std::string describeC(const struct InventoryContext *c)
1069 if(c->current_player == NULL)
1070 return "current_player=NULL";
1072 return std::string("current_player=") + c->current_player->getName();
1075 IMoveAction::IMoveAction(std::istream &is)
1079 std::getline(is, ts, ' ');
1082 std::getline(is, from_inv, ' ');
1084 std::getline(is, from_list, ' ');
1086 std::getline(is, ts, ' ');
1089 std::getline(is, to_inv, ' ');
1091 std::getline(is, to_list, ' ');
1093 std::getline(is, ts, ' ');
1097 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1098 ServerEnvironment *env)
1100 Inventory *inv_from = mgr->getInventory(c, from_inv);
1101 Inventory *inv_to = mgr->getInventory(c, to_inv);
1104 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1105 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1106 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1110 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1111 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1112 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1116 InventoryList *list_from = inv_from->getList(from_list);
1117 InventoryList *list_to = inv_to->getList(to_list);
1120 If a list doesn't exist or the source item doesn't exist
1123 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1124 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1125 <<", from_list=\""<<from_list<<"\""<<std::endl;
1129 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1130 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1131 <<", to_list=\""<<to_list<<"\""<<std::endl;
1134 if(list_from->getItem(from_i) == NULL)
1136 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1137 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1138 <<", from_list=\""<<from_list<<"\""
1139 <<" from_i="<<from_i<<std::endl;
1143 If the source and the destination slots are the same
1145 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1147 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1148 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1149 <<"\" i="<<from_i<<std::endl;
1153 // Take item from source list
1154 InventoryItem *item1 = NULL;
1156 item1 = list_from->changeItem(from_i, NULL);
1158 item1 = list_from->takeItem(from_i, count);
1160 // Try to add the item to destination list
1161 InventoryItem *olditem = item1;
1162 item1 = list_to->addItem(to_i, item1);
1164 // If something is returned, the item was not fully added
1167 // If olditem is returned, nothing was added.
1168 bool nothing_added = (item1 == olditem);
1170 // If something else is returned, part of the item was left unadded.
1171 // Add the other part back to the source item
1172 list_from->addItem(from_i, item1);
1174 // If olditem is returned, nothing was added.
1178 // Take item from source list
1179 item1 = list_from->changeItem(from_i, NULL);
1180 // Adding was not possible, swap the items.
1181 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1182 // Put item from destination list to the source list
1183 list_from->changeItem(from_i, item2);
1187 mgr->inventoryModified(c, from_inv);
1188 if(from_inv != to_inv)
1189 mgr->inventoryModified(c, to_inv);
1191 infostream<<"IMoveAction::apply(): moved at "
1192 <<"["<<describeC(c)<<"]"
1193 <<" from inv=\""<<from_inv<<"\""
1194 <<" list=\""<<from_list<<"\""
1196 <<" to inv=\""<<to_inv<<"\""
1197 <<" list=\""<<to_list<<"\""
1202 IDropAction::IDropAction(std::istream &is)
1206 std::getline(is, ts, ' ');
1209 std::getline(is, from_inv, ' ');
1211 std::getline(is, from_list, ' ');
1213 std::getline(is, ts, ' ');
1217 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1218 ServerEnvironment *env)
1220 if(c->current_player == NULL){
1221 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1225 // Do NOT cast directly to ServerActiveObject*, it breaks
1226 // because of multiple inheritance.
1227 ServerActiveObject *dropper =
1228 static_cast<ServerActiveObject*>(
1229 static_cast<ServerRemotePlayer*>(
1233 Inventory *inv_from = mgr->getInventory(c, from_inv);
1236 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1237 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1241 InventoryList *list_from = inv_from->getList(from_list);
1244 If a list doesn't exist or the source item doesn't exist
1247 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1248 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1249 <<", from_list=\""<<from_list<<"\""<<std::endl;
1252 InventoryItem *item = list_from->getItem(from_i);
1255 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1256 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1257 <<", from_list=\""<<from_list<<"\""
1258 <<" from_i="<<from_i<<std::endl;
1262 v3f pos = dropper->getBasePosition();
1272 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1274 list_from->deleteItem(from_i);
1276 mgr->inventoryModified(c, from_inv);
1278 infostream<<"IDropAction::apply(): dropped "
1279 <<"["<<describeC(c)<<"]"
1280 <<" from inv=\""<<from_inv<<"\""
1281 <<" list=\""<<from_list<<"\""
1287 Craft checking system
1290 bool ItemSpec::checkItem(const InventoryItem *item) const
1292 if(type == ITEM_NONE)
1294 // Has to be no item
1300 // There should be an item
1304 std::string itemname = item->getName();
1306 if(type == ITEM_MATERIAL)
1308 if(itemname != "MaterialItem")
1310 MaterialItem *mitem = (MaterialItem*)item;
1312 if(mitem->getMaterial() != num)
1315 if(mitem->getNodeName() != name)
1319 else if(type == ITEM_CRAFT)
1321 if(itemname != "CraftItem")
1323 CraftItem *mitem = (CraftItem*)item;
1324 if(mitem->getSubName() != name)
1327 else if(type == ITEM_TOOL)
1329 // Not supported yet
1332 else if(type == ITEM_MBO)
1334 // Not supported yet
1339 // Not supported yet
1345 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1347 u16 items_min_x = 100;
1348 u16 items_max_x = 100;
1349 u16 items_min_y = 100;
1350 u16 items_max_y = 100;
1351 for(u16 y=0; y<3; y++)
1352 for(u16 x=0; x<3; x++)
1354 if(items[y*3 + x] == NULL)
1356 if(items_min_x == 100 || x < items_min_x)
1358 if(items_min_y == 100 || y < items_min_y)
1360 if(items_max_x == 100 || x > items_max_x)
1362 if(items_max_y == 100 || y > items_max_y)
1365 // No items at all, just return false
1366 if(items_min_x == 100)
1369 u16 items_w = items_max_x - items_min_x + 1;
1370 u16 items_h = items_max_y - items_min_y + 1;
1372 u16 specs_min_x = 100;
1373 u16 specs_max_x = 100;
1374 u16 specs_min_y = 100;
1375 u16 specs_max_y = 100;
1376 for(u16 y=0; y<3; y++)
1377 for(u16 x=0; x<3; x++)
1379 if(specs[y*3 + x].type == ITEM_NONE)
1381 if(specs_min_x == 100 || x < specs_min_x)
1383 if(specs_min_y == 100 || y < specs_min_y)
1385 if(specs_max_x == 100 || x > specs_max_x)
1387 if(specs_max_y == 100 || y > specs_max_y)
1390 // No specs at all, just return false
1391 if(specs_min_x == 100)
1394 u16 specs_w = specs_max_x - specs_min_x + 1;
1395 u16 specs_h = specs_max_y - specs_min_y + 1;
1398 if(items_w != specs_w || items_h != specs_h)
1401 for(u16 y=0; y<specs_h; y++)
1402 for(u16 x=0; x<specs_w; x++)
1404 u16 items_x = items_min_x + x;
1405 u16 items_y = items_min_y + y;
1406 u16 specs_x = specs_min_x + x;
1407 u16 specs_y = specs_min_y + y;
1408 const InventoryItem *item = items[items_y * 3 + items_x];
1409 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1411 if(spec.checkItem(item) == false)
1418 bool checkItemCombination(const InventoryItem * const * items,
1419 const InventoryItem * const * specs)
1421 u16 items_min_x = 100;
1422 u16 items_max_x = 100;
1423 u16 items_min_y = 100;
1424 u16 items_max_y = 100;
1425 for(u16 y=0; y<3; y++)
1426 for(u16 x=0; x<3; x++)
1428 if(items[y*3 + x] == NULL)
1430 if(items_min_x == 100 || x < items_min_x)
1432 if(items_min_y == 100 || y < items_min_y)
1434 if(items_max_x == 100 || x > items_max_x)
1436 if(items_max_y == 100 || y > items_max_y)
1439 // No items at all, just return false
1440 if(items_min_x == 100)
1443 u16 items_w = items_max_x - items_min_x + 1;
1444 u16 items_h = items_max_y - items_min_y + 1;
1446 u16 specs_min_x = 100;
1447 u16 specs_max_x = 100;
1448 u16 specs_min_y = 100;
1449 u16 specs_max_y = 100;
1450 for(u16 y=0; y<3; y++)
1451 for(u16 x=0; x<3; x++)
1453 if(specs[y*3 + x] == NULL)
1455 if(specs_min_x == 100 || x < specs_min_x)
1457 if(specs_min_y == 100 || y < specs_min_y)
1459 if(specs_max_x == 100 || x > specs_max_x)
1461 if(specs_max_y == 100 || y > specs_max_y)
1464 // No specs at all, just return false
1465 if(specs_min_x == 100)
1468 u16 specs_w = specs_max_x - specs_min_x + 1;
1469 u16 specs_h = specs_max_y - specs_min_y + 1;
1472 if(items_w != specs_w || items_h != specs_h)
1475 for(u16 y=0; y<specs_h; y++)
1476 for(u16 x=0; x<specs_w; x++)
1478 u16 items_x = items_min_x + x;
1479 u16 items_y = items_min_y + y;
1480 u16 specs_x = specs_min_x + x;
1481 u16 specs_y = specs_min_y + y;
1482 const InventoryItem *item = items[items_y * 3 + items_x];
1483 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1485 if(item == NULL && spec == NULL)
1487 if(item == NULL && spec != NULL)
1489 if(item != NULL && spec == NULL)
1491 if(!spec->isSubsetOf(item))