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)
138 pos.Y -= BS*0.25; // let it drop a bit
140 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
141 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
143 ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
151 bool MaterialItem::isCookable() const
153 return item_material_is_cookable(m_content);
156 InventoryItem *MaterialItem::createCookResult() const
158 return item_material_create_cook_result(m_content);
166 video::ITexture * CraftItem::getImage() const
168 if(g_texturesource == NULL)
171 std::string name = item_craft_get_image_name(m_subname);
173 // Get such a texture
174 return g_texturesource->getTextureRaw(name);
178 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
181 ServerActiveObject *obj = item_craft_create_object(m_subname, env, pos);
185 return InventoryItem::createSAO(env, id, pos);
188 u16 CraftItem::getDropCount() const
191 s16 dc = item_craft_get_drop_count(m_subname);
195 return InventoryItem::getDropCount();
198 bool CraftItem::isCookable() const
200 return item_craft_is_cookable(m_subname);
203 InventoryItem *CraftItem::createCookResult() const
205 return item_craft_create_cook_result(m_subname);
208 bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
210 if(!item_craft_is_eatable(m_subname))
213 u16 result_count = getCount() - 1; // Eat one at a time
214 s16 hp_change = item_craft_eat_hp_change(m_subname);
215 s16 hp = user->getHP();
224 setCount(result_count);
232 InventoryList::InventoryList(std::string name, u32 size)
240 InventoryList::~InventoryList()
242 for(u32 i=0; i<m_items.size(); i++)
249 void InventoryList::clearItems()
251 for(u32 i=0; i<m_items.size(); i++)
259 for(u32 i=0; i<m_size; i++)
261 m_items.push_back(NULL);
267 void InventoryList::serialize(std::ostream &os) const
269 //os.imbue(std::locale("C"));
271 for(u32 i=0; i<m_items.size(); i++)
273 InventoryItem *item = m_items[i];
286 os<<"EndInventoryList\n";
289 void InventoryList::deSerialize(std::istream &is)
291 //is.imbue(std::locale("C"));
299 std::getline(is, line, '\n');
301 std::istringstream iss(line);
302 //iss.imbue(std::locale("C"));
305 std::getline(iss, name, ' ');
307 if(name == "EndInventoryList")
311 // This is a temporary backwards compatibility fix
312 else if(name == "end")
316 else if(name == "Item")
318 if(item_i > getSize() - 1)
319 throw SerializationError("too many items");
320 InventoryItem *item = InventoryItem::deSerialize(iss);
321 m_items[item_i++] = item;
323 else if(name == "Empty")
325 if(item_i > getSize() - 1)
326 throw SerializationError("too many items");
327 m_items[item_i++] = NULL;
331 throw SerializationError("Unknown inventory identifier");
336 InventoryList::InventoryList(const InventoryList &other)
339 Do this so that the items get cloned. Otherwise the pointers
340 in the array will just get copied.
345 InventoryList & InventoryList::operator = (const InventoryList &other)
347 m_name = other.m_name;
348 m_size = other.m_size;
350 for(u32 i=0; i<other.m_items.size(); i++)
352 InventoryItem *item = other.m_items[i];
355 m_items[i] = item->clone();
363 const std::string &InventoryList::getName() const
368 u32 InventoryList::getSize()
370 return m_items.size();
373 u32 InventoryList::getUsedSlots()
376 for(u32 i=0; i<m_items.size(); i++)
378 InventoryItem *item = m_items[i];
385 u32 InventoryList::getFreeSlots()
387 return getSize() - getUsedSlots();
390 const InventoryItem * InventoryList::getItem(u32 i) const
392 if(i > m_items.size() - 1)
397 InventoryItem * InventoryList::getItem(u32 i)
399 if(i > m_items.size() - 1)
404 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
406 assert(i < m_items.size());
408 InventoryItem *olditem = m_items[i];
409 m_items[i] = newitem;
414 void InventoryList::deleteItem(u32 i)
416 assert(i < m_items.size());
417 InventoryItem *item = changeItem(i, NULL);
422 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
428 First try to find if it could be added to some existing items
430 for(u32 i=0; i<m_items.size(); i++)
432 // Ignore empty slots
433 if(m_items[i] == NULL)
436 newitem = addItem(i, newitem);
438 return NULL; // All was eaten
442 Then try to add it to empty slots
444 for(u32 i=0; i<m_items.size(); i++)
446 // Ignore unempty slots
447 if(m_items[i] != NULL)
450 newitem = addItem(i, newitem);
452 return NULL; // All was eaten
459 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
466 // If it is an empty position, it's an easy job.
467 InventoryItem *to_item = getItem(i);
470 m_items[i] = newitem;
474 // If not addable, return the item
475 if(newitem->addableTo(to_item) == false)
478 // If the item fits fully in the slot, add counter and delete it
479 if(newitem->getCount() <= to_item->freeSpace())
481 to_item->add(newitem->getCount());
485 // Else the item does not fit fully. Add all that fits and return
489 u16 freespace = to_item->freeSpace();
490 to_item->add(freespace);
491 newitem->remove(freespace);
496 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
498 // If it is an empty position, it's an easy job.
499 const InventoryItem *to_item = getItem(i);
505 // If not addable, fail
506 if(newitem->addableTo(to_item) == false)
509 // If the item fits fully in the slot, pass
510 if(newitem->getCount() <= to_item->freeSpace())
518 bool InventoryList::roomForItem(const InventoryItem *item)
520 for(u32 i=0; i<m_items.size(); i++)
521 if(itemFits(i, item))
526 bool InventoryList::roomForCookedItem(const InventoryItem *item)
530 const InventoryItem *cook = item->createCookResult();
533 bool room = roomForItem(cook);
538 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
545 InventoryItem *item = getItem(i);
546 // If it is an empty position, return NULL
550 if(count >= item->getCount())
552 // Get the item by swapping NULL to its place
553 return changeItem(i, NULL);
557 InventoryItem *item2 = item->clone();
559 item2->setCount(count);
566 void InventoryList::decrementMaterials(u16 count)
568 for(u32 i=0; i<m_items.size(); i++)
570 InventoryItem *item = takeItem(i, count);
576 void InventoryList::print(std::ostream &o)
578 o<<"InventoryList:"<<std::endl;
579 for(u32 i=0; i<m_items.size(); i++)
581 InventoryItem *item = m_items[i];
595 Inventory::~Inventory()
600 void Inventory::clear()
602 for(u32 i=0; i<m_lists.size(); i++)
609 Inventory::Inventory()
613 Inventory::Inventory(const Inventory &other)
618 Inventory & Inventory::operator = (const Inventory &other)
621 for(u32 i=0; i<other.m_lists.size(); i++)
623 m_lists.push_back(new InventoryList(*other.m_lists[i]));
628 void Inventory::serialize(std::ostream &os) const
630 for(u32 i=0; i<m_lists.size(); i++)
632 InventoryList *list = m_lists[i];
633 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
637 os<<"EndInventory\n";
640 void Inventory::deSerialize(std::istream &is)
647 std::getline(is, line, '\n');
649 std::istringstream iss(line);
652 std::getline(iss, name, ' ');
654 if(name == "EndInventory")
658 // This is a temporary backwards compatibility fix
659 else if(name == "end")
663 else if(name == "List")
665 std::string listname;
668 std::getline(iss, listname, ' ');
671 InventoryList *list = new InventoryList(listname, listsize);
672 list->deSerialize(is);
674 m_lists.push_back(list);
678 throw SerializationError("Unknown inventory identifier");
683 InventoryList * Inventory::addList(const std::string &name, u32 size)
685 s32 i = getListIndex(name);
688 if(m_lists[i]->getSize() != size)
691 m_lists[i] = new InventoryList(name, size);
697 m_lists.push_back(new InventoryList(name, size));
698 return m_lists.getLast();
702 InventoryList * Inventory::getList(const std::string &name)
704 s32 i = getListIndex(name);
710 const InventoryList * Inventory::getList(const std::string &name) const
712 s32 i = getListIndex(name);
718 const s32 Inventory::getListIndex(const std::string &name) const
720 for(u32 i=0; i<m_lists.size(); i++)
722 if(m_lists[i]->getName() == name)
732 InventoryAction * InventoryAction::deSerialize(std::istream &is)
735 std::getline(is, type, ' ');
737 InventoryAction *a = NULL;
741 a = new IMoveAction(is);
747 static std::string describeC(const struct InventoryContext *c)
749 if(c->current_player == NULL)
750 return "current_player=NULL";
752 return std::string("current_player=") + c->current_player->getName();
755 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr)
757 Inventory *inv_from = mgr->getInventory(c, from_inv);
758 Inventory *inv_to = mgr->getInventory(c, to_inv);
761 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
762 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
763 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
767 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
768 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
769 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
773 InventoryList *list_from = inv_from->getList(from_list);
774 InventoryList *list_to = inv_to->getList(to_list);
777 If a list doesn't exist or the source item doesn't exist
780 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
781 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
782 <<", from_list=\""<<from_list<<"\""<<std::endl;
786 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
787 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
788 <<", to_list=\""<<to_list<<"\""<<std::endl;
791 if(list_from->getItem(from_i) == NULL)
793 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
794 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
795 <<", from_list=\""<<from_list<<"\""
796 <<" from_i="<<from_i<<std::endl;
800 If the source and the destination slots are the same
802 if(inv_from == inv_to && list_from == list_to && from_i == to_i)
804 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
805 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
806 <<"\" i="<<from_i<<std::endl;
810 // Take item from source list
811 InventoryItem *item1 = NULL;
813 item1 = list_from->changeItem(from_i, NULL);
815 item1 = list_from->takeItem(from_i, count);
817 // Try to add the item to destination list
818 InventoryItem *olditem = item1;
819 item1 = list_to->addItem(to_i, item1);
821 // If something is returned, the item was not fully added
824 // If olditem is returned, nothing was added.
825 bool nothing_added = (item1 == olditem);
827 // If something else is returned, part of the item was left unadded.
828 // Add the other part back to the source item
829 list_from->addItem(from_i, item1);
831 // If olditem is returned, nothing was added.
835 // Take item from source list
836 item1 = list_from->changeItem(from_i, NULL);
837 // Adding was not possible, swap the items.
838 InventoryItem *item2 = list_to->changeItem(to_i, item1);
839 // Put item from destination list to the source list
840 list_from->changeItem(from_i, item2);
844 mgr->inventoryModified(c, from_inv);
845 if(from_inv != to_inv)
846 mgr->inventoryModified(c, to_inv);
848 infostream<<"IMoveAction::apply(): moved at "
849 <<"["<<describeC(c)<<"]"
850 <<" from inv=\""<<from_inv<<"\""
851 <<" list=\""<<from_list<<"\""
853 <<" to inv=\""<<to_inv<<"\""
854 <<" list=\""<<to_list<<"\""
860 Craft checking system
863 bool ItemSpec::checkItem(const InventoryItem *item) const
865 if(type == ITEM_NONE)
873 // There should be an item
877 std::string itemname = item->getName();
879 if(type == ITEM_MATERIAL)
881 if(itemname != "MaterialItem")
883 MaterialItem *mitem = (MaterialItem*)item;
884 if(mitem->getMaterial() != num)
887 else if(type == ITEM_CRAFT)
889 if(itemname != "CraftItem")
891 CraftItem *mitem = (CraftItem*)item;
892 if(mitem->getSubName() != name)
895 else if(type == ITEM_TOOL)
900 else if(type == ITEM_MBO)
913 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
915 u16 items_min_x = 100;
916 u16 items_max_x = 100;
917 u16 items_min_y = 100;
918 u16 items_max_y = 100;
919 for(u16 y=0; y<3; y++)
920 for(u16 x=0; x<3; x++)
922 if(items[y*3 + x] == NULL)
924 if(items_min_x == 100 || x < items_min_x)
926 if(items_min_y == 100 || y < items_min_y)
928 if(items_max_x == 100 || x > items_max_x)
930 if(items_max_y == 100 || y > items_max_y)
933 // No items at all, just return false
934 if(items_min_x == 100)
937 u16 items_w = items_max_x - items_min_x + 1;
938 u16 items_h = items_max_y - items_min_y + 1;
940 u16 specs_min_x = 100;
941 u16 specs_max_x = 100;
942 u16 specs_min_y = 100;
943 u16 specs_max_y = 100;
944 for(u16 y=0; y<3; y++)
945 for(u16 x=0; x<3; x++)
947 if(specs[y*3 + x].type == ITEM_NONE)
949 if(specs_min_x == 100 || x < specs_min_x)
951 if(specs_min_y == 100 || y < specs_min_y)
953 if(specs_max_x == 100 || x > specs_max_x)
955 if(specs_max_y == 100 || y > specs_max_y)
958 // No specs at all, just return false
959 if(specs_min_x == 100)
962 u16 specs_w = specs_max_x - specs_min_x + 1;
963 u16 specs_h = specs_max_y - specs_min_y + 1;
966 if(items_w != specs_w || items_h != specs_h)
969 for(u16 y=0; y<specs_h; y++)
970 for(u16 x=0; x<specs_w; x++)
972 u16 items_x = items_min_x + x;
973 u16 items_y = items_min_y + y;
974 u16 specs_x = specs_min_x + x;
975 u16 specs_y = specs_min_y + y;
976 const InventoryItem *item = items[items_y * 3 + items_x];
977 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
979 if(spec.checkItem(item) == false)