3 Copyright (C) 2010 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.
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
24 #include "inventory.h"
25 #include "serialization.h"
30 #include "serverobject.h"
31 #include "content_mapnode.h"
32 #include "content_inventory.h"
33 #include "content_sao.h"
41 InventoryItem::InventoryItem(u16 count)
46 InventoryItem::~InventoryItem()
50 content_t content_translate_from_19_to_internal(content_t c_from)
52 for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
54 if(trans_table_19[i][1] == c_from)
56 return trans_table_19[i][0];
62 InventoryItem* InventoryItem::deSerialize(std::istream &is)
64 DSTACK(__FUNCTION_NAME);
66 //is.imbue(std::locale("C"));
69 std::getline(is, name, ' ');
71 if(name == "MaterialItem")
73 // u16 reads directly as a number (u8 doesn't)
78 // Convert old materials
81 material = content_translate_from_19_to_internal(material);
83 if(material > MAX_CONTENT)
84 throw SerializationError("Too large material number");
85 return new MaterialItem(material, count);
87 else if(name == "MaterialItem2")
93 if(material > MAX_CONTENT)
94 throw SerializationError("Too large material number");
95 return new MaterialItem(material, count);
97 else if(name == "MBOItem")
99 std::string inventorystring;
100 std::getline(is, inventorystring, '|');
101 throw SerializationError("MBOItem not supported anymore");
103 else if(name == "CraftItem")
106 std::getline(is, subname, ' ');
109 return new CraftItem(subname, count);
111 else if(name == "ToolItem")
113 std::string toolname;
114 std::getline(is, toolname, ' ');
117 return new ToolItem(toolname, wear);
121 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
122 throw SerializationError("Unknown InventoryItem name");
126 std::string InventoryItem::getItemString() {
128 std::ostringstream os(std::ios_base::binary);
133 ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
139 ServerActiveObject *obj = new ItemSAO(env, 0, pos, getItemString());
147 bool MaterialItem::isCookable() const
149 return item_material_is_cookable(m_content);
152 InventoryItem *MaterialItem::createCookResult() const
154 return item_material_create_cook_result(m_content);
162 video::ITexture * CraftItem::getImage() const
164 if(g_texturesource == NULL)
167 std::string name = item_craft_get_image_name(m_subname);
169 // Get such a texture
170 return g_texturesource->getTextureRaw(name);
174 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
177 ServerActiveObject *obj = item_craft_create_object(m_subname, env, id, pos);
181 return InventoryItem::createSAO(env, id, pos);
184 u16 CraftItem::getDropCount() const
187 s16 dc = item_craft_get_drop_count(m_subname);
191 return InventoryItem::getDropCount();
194 bool CraftItem::isCookable() const
196 return item_craft_is_cookable(m_subname);
199 InventoryItem *CraftItem::createCookResult() const
201 return item_craft_create_cook_result(m_subname);
204 bool CraftItem::use(ServerEnvironment *env, Player *player)
206 if(item_craft_is_eatable(m_subname))
208 u16 result_count = getCount() - 1; // Eat one at a time
209 s16 hp_change = item_craft_eat_hp_change(m_subname);
210 if(player->hp + hp_change > 20)
213 player->hp += hp_change;
218 setCount(result_count);
227 InventoryList::InventoryList(std::string name, u32 size)
235 InventoryList::~InventoryList()
237 for(u32 i=0; i<m_items.size(); i++)
244 void InventoryList::clearItems()
246 for(u32 i=0; i<m_items.size(); i++)
254 for(u32 i=0; i<m_size; i++)
256 m_items.push_back(NULL);
262 void InventoryList::serialize(std::ostream &os) const
264 //os.imbue(std::locale("C"));
266 for(u32 i=0; i<m_items.size(); i++)
268 InventoryItem *item = m_items[i];
281 os<<"EndInventoryList\n";
284 void InventoryList::deSerialize(std::istream &is)
286 //is.imbue(std::locale("C"));
294 std::getline(is, line, '\n');
296 std::istringstream iss(line);
297 //iss.imbue(std::locale("C"));
300 std::getline(iss, name, ' ');
302 if(name == "EndInventoryList")
306 // This is a temporary backwards compatibility fix
307 else if(name == "end")
311 else if(name == "Item")
313 if(item_i > getSize() - 1)
314 throw SerializationError("too many items");
315 InventoryItem *item = InventoryItem::deSerialize(iss);
316 m_items[item_i++] = item;
318 else if(name == "Empty")
320 if(item_i > getSize() - 1)
321 throw SerializationError("too many items");
322 m_items[item_i++] = NULL;
326 throw SerializationError("Unknown inventory identifier");
331 InventoryList::InventoryList(const InventoryList &other)
334 Do this so that the items get cloned. Otherwise the pointers
335 in the array will just get copied.
340 InventoryList & InventoryList::operator = (const InventoryList &other)
342 m_name = other.m_name;
343 m_size = other.m_size;
345 for(u32 i=0; i<other.m_items.size(); i++)
347 InventoryItem *item = other.m_items[i];
350 m_items[i] = item->clone();
358 const std::string &InventoryList::getName() const
363 u32 InventoryList::getSize()
365 return m_items.size();
368 u32 InventoryList::getUsedSlots()
371 for(u32 i=0; i<m_items.size(); i++)
373 InventoryItem *item = m_items[i];
380 u32 InventoryList::getFreeSlots()
382 return getSize() - getUsedSlots();
385 const InventoryItem * InventoryList::getItem(u32 i) const
387 if(i > m_items.size() - 1)
392 InventoryItem * InventoryList::getItem(u32 i)
394 if(i > m_items.size() - 1)
399 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
401 assert(i < m_items.size());
403 InventoryItem *olditem = m_items[i];
404 m_items[i] = newitem;
409 void InventoryList::deleteItem(u32 i)
411 assert(i < m_items.size());
412 InventoryItem *item = changeItem(i, NULL);
417 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
423 First try to find if it could be added to some existing items
425 for(u32 i=0; i<m_items.size(); i++)
427 // Ignore empty slots
428 if(m_items[i] == NULL)
431 newitem = addItem(i, newitem);
433 return NULL; // All was eaten
437 Then try to add it to empty slots
439 for(u32 i=0; i<m_items.size(); i++)
441 // Ignore unempty slots
442 if(m_items[i] != NULL)
445 newitem = addItem(i, newitem);
447 return NULL; // All was eaten
454 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
461 // If it is an empty position, it's an easy job.
462 InventoryItem *to_item = getItem(i);
465 m_items[i] = newitem;
469 // If not addable, return the item
470 if(newitem->addableTo(to_item) == false)
473 // If the item fits fully in the slot, add counter and delete it
474 if(newitem->getCount() <= to_item->freeSpace())
476 to_item->add(newitem->getCount());
480 // Else the item does not fit fully. Add all that fits and return
484 u16 freespace = to_item->freeSpace();
485 to_item->add(freespace);
486 newitem->remove(freespace);
491 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
493 // If it is an empty position, it's an easy job.
494 const InventoryItem *to_item = getItem(i);
500 // If not addable, fail
501 if(newitem->addableTo(to_item) == false)
504 // If the item fits fully in the slot, pass
505 if(newitem->getCount() <= to_item->freeSpace())
513 bool InventoryList::roomForItem(const InventoryItem *item)
515 for(u32 i=0; i<m_items.size(); i++)
516 if(itemFits(i, item))
521 bool InventoryList::roomForCookedItem(const InventoryItem *item)
525 const InventoryItem *cook = item->createCookResult();
528 bool room = roomForItem(cook);
533 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
540 InventoryItem *item = getItem(i);
541 // If it is an empty position, return NULL
545 if(count >= item->getCount())
547 // Get the item by swapping NULL to its place
548 return changeItem(i, NULL);
552 InventoryItem *item2 = item->clone();
554 item2->setCount(count);
561 void InventoryList::decrementMaterials(u16 count)
563 for(u32 i=0; i<m_items.size(); i++)
565 InventoryItem *item = takeItem(i, count);
571 void InventoryList::print(std::ostream &o)
573 o<<"InventoryList:"<<std::endl;
574 for(u32 i=0; i<m_items.size(); i++)
576 InventoryItem *item = m_items[i];
590 Inventory::~Inventory()
595 void Inventory::clear()
597 for(u32 i=0; i<m_lists.size(); i++)
604 Inventory::Inventory()
608 Inventory::Inventory(const Inventory &other)
613 Inventory & Inventory::operator = (const Inventory &other)
616 for(u32 i=0; i<other.m_lists.size(); i++)
618 m_lists.push_back(new InventoryList(*other.m_lists[i]));
623 void Inventory::serialize(std::ostream &os) const
625 for(u32 i=0; i<m_lists.size(); i++)
627 InventoryList *list = m_lists[i];
628 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
632 os<<"EndInventory\n";
635 void Inventory::deSerialize(std::istream &is)
642 std::getline(is, line, '\n');
644 std::istringstream iss(line);
647 std::getline(iss, name, ' ');
649 if(name == "EndInventory")
653 // This is a temporary backwards compatibility fix
654 else if(name == "end")
658 else if(name == "List")
660 std::string listname;
663 std::getline(iss, listname, ' ');
666 InventoryList *list = new InventoryList(listname, listsize);
667 list->deSerialize(is);
669 m_lists.push_back(list);
673 throw SerializationError("Unknown inventory identifier");
678 InventoryList * Inventory::addList(const std::string &name, u32 size)
680 s32 i = getListIndex(name);
683 if(m_lists[i]->getSize() != size)
686 m_lists[i] = new InventoryList(name, size);
692 m_lists.push_back(new InventoryList(name, size));
693 return m_lists.getLast();
697 InventoryList * Inventory::getList(const std::string &name)
699 s32 i = getListIndex(name);
705 const InventoryList * Inventory::getList(const std::string &name) const
707 s32 i = getListIndex(name);
713 const s32 Inventory::getListIndex(const std::string &name) const
715 for(u32 i=0; i<m_lists.size(); i++)
717 if(m_lists[i]->getName() == name)
727 InventoryAction * InventoryAction::deSerialize(std::istream &is)
730 std::getline(is, type, ' ');
732 InventoryAction *a = NULL;
736 a = new IMoveAction(is);
742 static std::string describeC(const struct InventoryContext *c)
744 if(c->current_player == NULL)
745 return "current_player=NULL";
747 return std::string("current_player=") + c->current_player->getName();
750 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr)
752 Inventory *inv_from = mgr->getInventory(c, from_inv);
753 Inventory *inv_to = mgr->getInventory(c, to_inv);
756 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
757 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
758 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
762 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
763 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
764 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
768 InventoryList *list_from = inv_from->getList(from_list);
769 InventoryList *list_to = inv_to->getList(to_list);
772 If a list doesn't exist or the source item doesn't exist
775 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
776 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
777 <<", from_list=\""<<from_list<<"\""<<std::endl;
781 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
782 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
783 <<", to_list=\""<<to_list<<"\""<<std::endl;
786 if(list_from->getItem(from_i) == NULL)
788 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
789 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
790 <<", from_list=\""<<from_list<<"\""
791 <<" from_i="<<from_i<<std::endl;
795 If the source and the destination slots are the same
797 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
799 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
800 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
801 <<"\" i="<<from_i<<std::endl;
805 // Take item from source list
806 InventoryItem *item1 = NULL;
808 item1 = list_from->changeItem(from_i, NULL);
810 item1 = list_from->takeItem(from_i, count);
812 // Try to add the item to destination list
813 InventoryItem *olditem = item1;
814 item1 = list_to->addItem(to_i, item1);
816 // If something is returned, the item was not fully added
819 // If olditem is returned, nothing was added.
820 bool nothing_added = (item1 == olditem);
822 // If something else is returned, part of the item was left unadded.
823 // Add the other part back to the source item
824 list_from->addItem(from_i, item1);
826 // If olditem is returned, nothing was added.
830 // Take item from source list
831 item1 = list_from->changeItem(from_i, NULL);
832 // Adding was not possible, swap the items.
833 InventoryItem *item2 = list_to->changeItem(to_i, item1);
834 // Put item from destination list to the source list
835 list_from->changeItem(from_i, item2);
839 mgr->inventoryModified(c, from_inv);
840 if(from_inv != to_inv)
841 mgr->inventoryModified(c, to_inv);
843 infostream<<"IMoveAction::apply(): moved at "
844 <<"["<<describeC(c)<<"]"
845 <<" from inv=\""<<from_inv<<"\""
846 <<" list=\""<<from_list<<"\""
848 <<" to inv=\""<<to_inv<<"\""
849 <<" list=\""<<to_list<<"\""
855 Craft checking system
858 bool ItemSpec::checkItem(const InventoryItem *item) const
860 if(type == ITEM_NONE)
868 // There should be an item
872 std::string itemname = item->getName();
874 if(type == ITEM_MATERIAL)
876 if(itemname != "MaterialItem")
878 MaterialItem *mitem = (MaterialItem*)item;
879 if(mitem->getMaterial() != num)
882 else if(type == ITEM_CRAFT)
884 if(itemname != "CraftItem")
886 CraftItem *mitem = (CraftItem*)item;
887 if(mitem->getSubName() != name)
890 else if(type == ITEM_TOOL)
895 else if(type == ITEM_MBO)
908 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
910 u16 items_min_x = 100;
911 u16 items_max_x = 100;
912 u16 items_min_y = 100;
913 u16 items_max_y = 100;
914 for(u16 y=0; y<3; y++)
915 for(u16 x=0; x<3; x++)
917 if(items[y*3 + x] == NULL)
919 if(items_min_x == 100 || x < items_min_x)
921 if(items_min_y == 100 || y < items_min_y)
923 if(items_max_x == 100 || x > items_max_x)
925 if(items_max_y == 100 || y > items_max_y)
928 // No items at all, just return false
929 if(items_min_x == 100)
932 u16 items_w = items_max_x - items_min_x + 1;
933 u16 items_h = items_max_y - items_min_y + 1;
935 u16 specs_min_x = 100;
936 u16 specs_max_x = 100;
937 u16 specs_min_y = 100;
938 u16 specs_max_y = 100;
939 for(u16 y=0; y<3; y++)
940 for(u16 x=0; x<3; x++)
942 if(specs[y*3 + x].type == ITEM_NONE)
944 if(specs_min_x == 100 || x < specs_min_x)
946 if(specs_min_y == 100 || y < specs_min_y)
948 if(specs_max_x == 100 || x > specs_max_x)
950 if(specs_max_y == 100 || y > specs_max_y)
953 // No specs at all, just return false
954 if(specs_min_x == 100)
957 u16 specs_w = specs_max_x - specs_min_x + 1;
958 u16 specs_h = specs_max_y - specs_min_y + 1;
961 if(items_w != specs_w || items_h != specs_h)
964 for(u16 y=0; y<specs_h; y++)
965 for(u16 x=0; x<specs_w; x++)
967 u16 items_x = items_min_x + x;
968 u16 items_y = items_min_y + y;
969 u16 specs_x = specs_min_x + x;
970 u16 specs_y = specs_min_y + y;
971 const InventoryItem *item = items[items_y * 3 + items_x];
972 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
974 if(spec.checkItem(item) == false)