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);
357 CraftItem::CraftItem(IGameDef *gamedef, std::string subname, u16 count):
358 InventoryItem(gamedef, count)
360 // Convert directly to the correct name through aliases.
361 // This is necessary because CraftItem callbacks are stored in
362 // Lua refenced by their correct name
363 m_subname = gamedef->cidef()->getAlias(subname);
367 video::ITexture * CraftItem::getImage() const
369 ICraftItemDefManager *cidef = m_gamedef->cidef();
370 ITextureSource *tsrc = m_gamedef->tsrc();
371 std::string imagename = cidef->getImagename(m_subname);
372 return tsrc->getTextureRaw(imagename);
376 bool CraftItem::isKnown() const
378 ICraftItemDefManager *cidef = m_gamedef->cidef();
379 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
380 return (def != NULL);
383 u16 CraftItem::getStackMax() const
385 ICraftItemDefManager *cidef = m_gamedef->cidef();
386 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
388 return InventoryItem::getStackMax();
389 return def->stack_max;
392 bool CraftItem::isUsable() const
394 ICraftItemDefManager *cidef = m_gamedef->cidef();
395 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
396 return def != NULL && def->usable;
399 bool CraftItem::isCookable() const
401 ICraftItemDefManager *cidef = m_gamedef->cidef();
402 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
403 return def != NULL && def->cookresult_item != "";
406 InventoryItem *CraftItem::createCookResult() const
408 ICraftItemDefManager *cidef = m_gamedef->cidef();
409 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
411 return InventoryItem::createCookResult();
412 std::istringstream is(def->cookresult_item, std::ios::binary);
413 return InventoryItem::deSerialize(is, m_gamedef);
416 float CraftItem::getCookTime() const
418 ICraftItemDefManager *cidef = m_gamedef->cidef();
419 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
421 return InventoryItem::getCookTime();
422 return def->furnace_cooktime;
425 float CraftItem::getBurnTime() const
427 ICraftItemDefManager *cidef = m_gamedef->cidef();
428 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
430 return InventoryItem::getBurnTime();
431 return def->furnace_burntime;
434 s16 CraftItem::getDropCount() const
437 ICraftItemDefManager *cidef = m_gamedef->cidef();
438 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
439 if(def != NULL && def->dropcount >= 0)
440 return def->dropcount;
442 return InventoryItem::getDropCount();
445 bool CraftItem::areLiquidsPointable() const
447 ICraftItemDefManager *cidef = m_gamedef->cidef();
448 const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
449 return def != NULL && def->liquids_pointable;
452 bool CraftItem::dropOrPlace(ServerEnvironment *env,
453 ServerActiveObject *dropper,
454 v3f pos, bool place, s16 count)
459 bool callback_exists = false;
464 result = scriptapi_craftitem_on_place_on_ground(
466 m_subname.c_str(), dropper, pos,
470 // note: on_drop is fallback for on_place_on_ground
474 result = scriptapi_craftitem_on_drop(
476 m_subname.c_str(), dropper, pos,
482 // If the callback returned true, drop one item
484 setCount(getCount() - 1);
485 return getCount() < 1;
489 // If neither on_place_on_ground (if place==true)
490 // nor on_drop exists, call the base implementation
491 return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
495 bool CraftItem::use(ServerEnvironment *env,
496 ServerActiveObject *user,
497 const PointedThing& pointed)
499 bool callback_exists = false;
502 result = scriptapi_craftitem_on_use(
504 m_subname.c_str(), user, pointed,
509 // If the callback returned true, drop one item
511 setCount(getCount() - 1);
512 return getCount() < 1;
516 // If neither on_place_on_ground (if place==true)
517 // nor on_drop exists, call the base implementation
518 return InventoryItem::use(env, user, pointed);
526 InventoryList::InventoryList(std::string name, u32 size)
534 InventoryList::~InventoryList()
536 for(u32 i=0; i<m_items.size(); i++)
543 void InventoryList::clearItems()
545 for(u32 i=0; i<m_items.size(); i++)
553 for(u32 i=0; i<m_size; i++)
555 m_items.push_back(NULL);
561 void InventoryList::serialize(std::ostream &os) const
563 //os.imbue(std::locale("C"));
565 for(u32 i=0; i<m_items.size(); i++)
567 InventoryItem *item = m_items[i];
580 os<<"EndInventoryList\n";
583 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
585 //is.imbue(std::locale("C"));
593 std::getline(is, line, '\n');
595 std::istringstream iss(line);
596 //iss.imbue(std::locale("C"));
599 std::getline(iss, name, ' ');
601 if(name == "EndInventoryList")
605 // This is a temporary backwards compatibility fix
606 else if(name == "end")
610 else if(name == "Item")
612 if(item_i > getSize() - 1)
613 throw SerializationError("too many items");
614 InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
615 m_items[item_i++] = item;
617 else if(name == "Empty")
619 if(item_i > getSize() - 1)
620 throw SerializationError("too many items");
621 m_items[item_i++] = NULL;
625 throw SerializationError("Unknown inventory identifier");
630 InventoryList::InventoryList(const InventoryList &other)
633 Do this so that the items get cloned. Otherwise the pointers
634 in the array will just get copied.
639 InventoryList & InventoryList::operator = (const InventoryList &other)
641 m_name = other.m_name;
642 m_size = other.m_size;
644 for(u32 i=0; i<other.m_items.size(); i++)
646 InventoryItem *item = other.m_items[i];
649 m_items[i] = item->clone();
657 const std::string &InventoryList::getName() const
662 u32 InventoryList::getSize()
664 return m_items.size();
667 u32 InventoryList::getUsedSlots()
670 for(u32 i=0; i<m_items.size(); i++)
672 InventoryItem *item = m_items[i];
679 u32 InventoryList::getFreeSlots()
681 return getSize() - getUsedSlots();
684 const InventoryItem * InventoryList::getItem(u32 i) const
686 if(i >= m_items.size())
691 InventoryItem * InventoryList::getItem(u32 i)
693 if(i >= m_items.size())
698 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
700 if(i >= m_items.size())
703 InventoryItem *olditem = m_items[i];
704 m_items[i] = newitem;
709 void InventoryList::deleteItem(u32 i)
711 assert(i < m_items.size());
712 InventoryItem *item = changeItem(i, NULL);
717 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
723 First try to find if it could be added to some existing items
725 for(u32 i=0; i<m_items.size(); i++)
727 // Ignore empty slots
728 if(m_items[i] == NULL)
731 newitem = addItem(i, newitem);
733 return NULL; // All was eaten
737 Then try to add it to empty slots
739 for(u32 i=0; i<m_items.size(); i++)
741 // Ignore unempty slots
742 if(m_items[i] != NULL)
745 newitem = addItem(i, newitem);
747 return NULL; // All was eaten
754 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
758 if(i >= m_items.size())
763 // If it is an empty position, it's an easy job.
764 InventoryItem *to_item = getItem(i);
767 m_items[i] = newitem;
771 // If not addable, return the item
772 if(newitem->addableTo(to_item) == false)
775 // If the item fits fully in the slot, add counter and delete it
776 if(newitem->getCount() <= to_item->freeSpace())
778 to_item->add(newitem->getCount());
782 // Else the item does not fit fully. Add all that fits and return
786 u16 freespace = to_item->freeSpace();
787 to_item->add(freespace);
788 newitem->remove(freespace);
793 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
795 // If it is an empty position, it's an easy job.
796 const InventoryItem *to_item = getItem(i);
802 // If not addable, fail
803 if(newitem->addableTo(to_item) == false)
806 // If the item fits fully in the slot, pass
807 if(newitem->getCount() <= to_item->freeSpace())
815 bool InventoryList::roomForItem(const InventoryItem *item)
817 for(u32 i=0; i<m_items.size(); i++)
818 if(itemFits(i, item))
823 bool InventoryList::roomForCookedItem(const InventoryItem *item)
827 const InventoryItem *cook = item->createCookResult();
830 bool room = roomForItem(cook);
835 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
842 InventoryItem *item = getItem(i);
843 // If it is an empty position, return NULL
847 if(count >= item->getCount())
849 // Get the item by swapping NULL to its place
850 return changeItem(i, NULL);
854 InventoryItem *item2 = item->clone();
856 item2->setCount(count);
863 void InventoryList::decrementMaterials(u16 count)
865 for(u32 i=0; i<m_items.size(); i++)
867 InventoryItem *item = takeItem(i, count);
873 void InventoryList::print(std::ostream &o)
875 o<<"InventoryList:"<<std::endl;
876 for(u32 i=0; i<m_items.size(); i++)
878 InventoryItem *item = m_items[i];
892 Inventory::~Inventory()
897 void Inventory::clear()
899 for(u32 i=0; i<m_lists.size(); i++)
906 Inventory::Inventory()
910 Inventory::Inventory(const Inventory &other)
915 Inventory & Inventory::operator = (const Inventory &other)
918 for(u32 i=0; i<other.m_lists.size(); i++)
920 m_lists.push_back(new InventoryList(*other.m_lists[i]));
925 void Inventory::serialize(std::ostream &os) const
927 for(u32 i=0; i<m_lists.size(); i++)
929 InventoryList *list = m_lists[i];
930 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
934 os<<"EndInventory\n";
937 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
944 std::getline(is, line, '\n');
946 std::istringstream iss(line);
949 std::getline(iss, name, ' ');
951 if(name == "EndInventory")
955 // This is a temporary backwards compatibility fix
956 else if(name == "end")
960 else if(name == "List")
962 std::string listname;
965 std::getline(iss, listname, ' ');
968 InventoryList *list = new InventoryList(listname, listsize);
969 list->deSerialize(is, gamedef);
971 m_lists.push_back(list);
975 throw SerializationError("Unknown inventory identifier");
980 InventoryList * Inventory::addList(const std::string &name, u32 size)
982 s32 i = getListIndex(name);
985 if(m_lists[i]->getSize() != size)
988 m_lists[i] = new InventoryList(name, size);
994 m_lists.push_back(new InventoryList(name, size));
995 return m_lists.getLast();
999 InventoryList * Inventory::getList(const std::string &name)
1001 s32 i = getListIndex(name);
1007 bool Inventory::deleteList(const std::string &name)
1009 s32 i = getListIndex(name);
1017 const InventoryList * Inventory::getList(const std::string &name) const
1019 s32 i = getListIndex(name);
1025 const s32 Inventory::getListIndex(const std::string &name) const
1027 for(u32 i=0; i<m_lists.size(); i++)
1029 if(m_lists[i]->getName() == name)
1039 InventoryAction * InventoryAction::deSerialize(std::istream &is)
1042 std::getline(is, type, ' ');
1044 InventoryAction *a = NULL;
1048 a = new IMoveAction(is);
1050 else if(type == "Drop")
1052 a = new IDropAction(is);
1058 static std::string describeC(const struct InventoryContext *c)
1060 if(c->current_player == NULL)
1061 return "current_player=NULL";
1063 return std::string("current_player=") + c->current_player->getName();
1066 IMoveAction::IMoveAction(std::istream &is)
1070 std::getline(is, ts, ' ');
1073 std::getline(is, from_inv, ' ');
1075 std::getline(is, from_list, ' ');
1077 std::getline(is, ts, ' ');
1080 std::getline(is, to_inv, ' ');
1082 std::getline(is, to_list, ' ');
1084 std::getline(is, ts, ' ');
1088 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
1089 ServerEnvironment *env)
1091 Inventory *inv_from = mgr->getInventory(c, from_inv);
1092 Inventory *inv_to = mgr->getInventory(c, to_inv);
1095 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
1096 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1097 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1101 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
1102 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1103 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
1107 InventoryList *list_from = inv_from->getList(from_list);
1108 InventoryList *list_to = inv_to->getList(to_list);
1111 If a list doesn't exist or the source item doesn't exist
1114 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
1115 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1116 <<", from_list=\""<<from_list<<"\""<<std::endl;
1120 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
1121 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
1122 <<", to_list=\""<<to_list<<"\""<<std::endl;
1125 if(list_from->getItem(from_i) == NULL)
1127 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
1128 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1129 <<", from_list=\""<<from_list<<"\""
1130 <<" from_i="<<from_i<<std::endl;
1134 If the source and the destination slots are the same
1136 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
1138 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
1139 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
1140 <<"\" i="<<from_i<<std::endl;
1144 // Take item from source list
1145 InventoryItem *item1 = NULL;
1147 item1 = list_from->changeItem(from_i, NULL);
1149 item1 = list_from->takeItem(from_i, count);
1151 // Try to add the item to destination list
1152 InventoryItem *olditem = item1;
1153 item1 = list_to->addItem(to_i, item1);
1155 // If something is returned, the item was not fully added
1158 // If olditem is returned, nothing was added.
1159 bool nothing_added = (item1 == olditem);
1161 // If something else is returned, part of the item was left unadded.
1162 // Add the other part back to the source item
1163 list_from->addItem(from_i, item1);
1165 // If olditem is returned, nothing was added.
1169 // Take item from source list
1170 item1 = list_from->changeItem(from_i, NULL);
1171 // Adding was not possible, swap the items.
1172 InventoryItem *item2 = list_to->changeItem(to_i, item1);
1173 // Put item from destination list to the source list
1174 list_from->changeItem(from_i, item2);
1178 mgr->inventoryModified(c, from_inv);
1179 if(from_inv != to_inv)
1180 mgr->inventoryModified(c, to_inv);
1182 infostream<<"IMoveAction::apply(): moved at "
1183 <<"["<<describeC(c)<<"]"
1184 <<" from inv=\""<<from_inv<<"\""
1185 <<" list=\""<<from_list<<"\""
1187 <<" to inv=\""<<to_inv<<"\""
1188 <<" list=\""<<to_list<<"\""
1193 IDropAction::IDropAction(std::istream &is)
1197 std::getline(is, ts, ' ');
1200 std::getline(is, from_inv, ' ');
1202 std::getline(is, from_list, ' ');
1204 std::getline(is, ts, ' ');
1208 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1209 ServerEnvironment *env)
1211 if(c->current_player == NULL){
1212 infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
1216 // Do NOT cast directly to ServerActiveObject*, it breaks
1217 // because of multiple inheritance.
1218 ServerActiveObject *dropper =
1219 static_cast<ServerActiveObject*>(
1220 static_cast<ServerRemotePlayer*>(
1224 Inventory *inv_from = mgr->getInventory(c, from_inv);
1227 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1228 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1232 InventoryList *list_from = inv_from->getList(from_list);
1235 If a list doesn't exist or the source item doesn't exist
1238 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1239 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1240 <<", from_list=\""<<from_list<<"\""<<std::endl;
1243 InventoryItem *item = list_from->getItem(from_i);
1246 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1247 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1248 <<", from_list=\""<<from_list<<"\""
1249 <<" from_i="<<from_i<<std::endl;
1253 v3f pos = dropper->getBasePosition();
1263 bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
1265 list_from->deleteItem(from_i);
1267 mgr->inventoryModified(c, from_inv);
1269 infostream<<"IDropAction::apply(): dropped "
1270 <<"["<<describeC(c)<<"]"
1271 <<" from inv=\""<<from_inv<<"\""
1272 <<" list=\""<<from_list<<"\""
1278 Craft checking system
1281 bool ItemSpec::checkItem(const InventoryItem *item) const
1283 if(type == ITEM_NONE)
1285 // Has to be no item
1291 // There should be an item
1295 std::string itemname = item->getName();
1297 if(type == ITEM_MATERIAL)
1299 if(itemname != "MaterialItem")
1301 MaterialItem *mitem = (MaterialItem*)item;
1303 if(mitem->getMaterial() != num)
1306 if(mitem->getNodeName() != name)
1310 else if(type == ITEM_CRAFT)
1312 if(itemname != "CraftItem")
1314 CraftItem *mitem = (CraftItem*)item;
1315 if(mitem->getSubName() != name)
1318 else if(type == ITEM_TOOL)
1320 // Not supported yet
1323 else if(type == ITEM_MBO)
1325 // Not supported yet
1330 // Not supported yet
1336 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1338 u16 items_min_x = 100;
1339 u16 items_max_x = 100;
1340 u16 items_min_y = 100;
1341 u16 items_max_y = 100;
1342 for(u16 y=0; y<3; y++)
1343 for(u16 x=0; x<3; x++)
1345 if(items[y*3 + x] == NULL)
1347 if(items_min_x == 100 || x < items_min_x)
1349 if(items_min_y == 100 || y < items_min_y)
1351 if(items_max_x == 100 || x > items_max_x)
1353 if(items_max_y == 100 || y > items_max_y)
1356 // No items at all, just return false
1357 if(items_min_x == 100)
1360 u16 items_w = items_max_x - items_min_x + 1;
1361 u16 items_h = items_max_y - items_min_y + 1;
1363 u16 specs_min_x = 100;
1364 u16 specs_max_x = 100;
1365 u16 specs_min_y = 100;
1366 u16 specs_max_y = 100;
1367 for(u16 y=0; y<3; y++)
1368 for(u16 x=0; x<3; x++)
1370 if(specs[y*3 + x].type == ITEM_NONE)
1372 if(specs_min_x == 100 || x < specs_min_x)
1374 if(specs_min_y == 100 || y < specs_min_y)
1376 if(specs_max_x == 100 || x > specs_max_x)
1378 if(specs_max_y == 100 || y > specs_max_y)
1381 // No specs at all, just return false
1382 if(specs_min_x == 100)
1385 u16 specs_w = specs_max_x - specs_min_x + 1;
1386 u16 specs_h = specs_max_y - specs_min_y + 1;
1389 if(items_w != specs_w || items_h != specs_h)
1392 for(u16 y=0; y<specs_h; y++)
1393 for(u16 x=0; x<specs_w; x++)
1395 u16 items_x = items_min_x + x;
1396 u16 items_y = items_min_y + y;
1397 u16 specs_x = specs_min_x + x;
1398 u16 specs_y = specs_min_y + y;
1399 const InventoryItem *item = items[items_y * 3 + items_x];
1400 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1402 if(spec.checkItem(item) == false)
1409 bool checkItemCombination(const InventoryItem * const * items,
1410 const InventoryItem * const * specs)
1412 u16 items_min_x = 100;
1413 u16 items_max_x = 100;
1414 u16 items_min_y = 100;
1415 u16 items_max_y = 100;
1416 for(u16 y=0; y<3; y++)
1417 for(u16 x=0; x<3; x++)
1419 if(items[y*3 + x] == NULL)
1421 if(items_min_x == 100 || x < items_min_x)
1423 if(items_min_y == 100 || y < items_min_y)
1425 if(items_max_x == 100 || x > items_max_x)
1427 if(items_max_y == 100 || y > items_max_y)
1430 // No items at all, just return false
1431 if(items_min_x == 100)
1434 u16 items_w = items_max_x - items_min_x + 1;
1435 u16 items_h = items_max_y - items_min_y + 1;
1437 u16 specs_min_x = 100;
1438 u16 specs_max_x = 100;
1439 u16 specs_min_y = 100;
1440 u16 specs_max_y = 100;
1441 for(u16 y=0; y<3; y++)
1442 for(u16 x=0; x<3; x++)
1444 if(specs[y*3 + x] == NULL)
1446 if(specs_min_x == 100 || x < specs_min_x)
1448 if(specs_min_y == 100 || y < specs_min_y)
1450 if(specs_max_x == 100 || x > specs_max_x)
1452 if(specs_max_y == 100 || y > specs_max_y)
1455 // No specs at all, just return false
1456 if(specs_min_x == 100)
1459 u16 specs_w = specs_max_x - specs_min_x + 1;
1460 u16 specs_h = specs_max_y - specs_min_y + 1;
1463 if(items_w != specs_w || items_h != specs_h)
1466 for(u16 y=0; y<specs_h; y++)
1467 for(u16 x=0; x<specs_w; x++)
1469 u16 items_x = items_min_x + x;
1470 u16 items_y = items_min_y + y;
1471 u16 specs_x = specs_min_x + x;
1472 u16 specs_y = specs_min_y + y;
1473 const InventoryItem *item = items[items_y * 3 + items_x];
1474 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1476 if(item == NULL && spec == NULL)
1478 if(item == NULL && spec != NULL)
1480 if(item != NULL && spec == NULL)
1482 if(!spec->isSubsetOf(item))