/************************************************************************ * Minetest-c55 * Copyright (C) 2010-2011 celeron55, Perttu Ahola * * inventory.h * voxelands - 3d voxel world sandbox game * Copyright (C) Lisa 'darkrose' Milne 2013-2014 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * * License updated from GPLv2 or later to GPLv3 or later by Lisa Milne * for Voxelands. ************************************************************************/ #ifndef INVENTORY_HEADER #define INVENTORY_HEADER #include #include #include #include #include "common_irrlicht.h" #include "debug.h" #include "main.h" // For g_materials #include "mapnode.h" // For content_t // for mapping to old subname #include "content_craftitem.h" #include "content_toolitem.h" #include "content_clothesitem.h" #include "content_mapnode.h" #define QUANTITY_ITEM_MAX_COUNT 99 class ServerActiveObject; class ServerEnvironment; class Player; class InventoryItem { public: InventoryItem(u16 count, u16 data); virtual ~InventoryItem(); static content_t info(std::istream &is, u16 *count, u16 *wear, u16 *data); static InventoryItem* deSerialize(std::istream &is); static InventoryItem* create(content_t c, u16 count, u16 wear=0, u16 data=0); virtual const char* getName() const = 0; // Shall write the name and the parameters virtual void serialize(std::ostream &os) const = 0; // Shall make an exact clone of the item virtual InventoryItem* clone() = 0; #ifndef SERVER // Return the name of the image for this item virtual std::string getBasename() const { return ""; } // Shall return an image of the item (or NULL) virtual video::ITexture * getImage() const { return NULL; } // Shall return an image of the item without embellishments (or NULL) virtual video::ITexture * getImageRaw() const { return getImage(); } #endif // get the content type content_t getContent() {return m_content;} // this is used for tool tips virtual std::wstring getGuiName() { return L""; } // this is used for hover data / extended tool tips virtual std::wstring getGuiText() { return L""; } // Shall return a text to show in the GUI virtual std::string getText() { return ""; } // Returns the string used for inventory virtual std::string getItemString(); // Creates an object from the item, to be placed in the world. virtual ServerActiveObject* createSAO(ServerEnvironment *env, u16 id, v3f pos) {return NULL;} // Gets amount of items that dropping one SAO will decrement virtual u16 getDropCount() const { return getCount(); } /* Quantity methods */ // Shall return true if the item can be add()ed to the other virtual bool addableTo(const InventoryItem *other) const { return false; } u16 getCount() const { return m_count; } void setCount(u16 count) { m_count = count; } // This should return something else for stackable items virtual u16 freeSpace() const { return 0; } void add(u16 count) { if (m_count + count > QUANTITY_ITEM_MAX_COUNT) return; m_count += count; } void remove(u16 count) { if (m_count < count) { m_count = 0; }else{ m_count -= count; } } virtual void setWear(u16 wear) {} virtual u16 getWear() {return 0;} void setData(u16 data) { m_data = data; } void addData(u16 data) { m_data |= data; } u16 getData() { return m_data; } /* Other properties */ // Whether it can be cooked virtual bool isCookable(uint16_t type=COOK_ANY) const {return false;} // Whether it can be crushed virtual bool isCrushable(CrushType type=CRUSH_ANY) const {return false;} // Time of cooking virtual float getCookTime() const {return 3.0;} // Result of cooking (can randomize) virtual InventoryItem *createCookResult() const {return NULL;} // Result of crushing (can randomize) virtual InventoryItem *createCrushResult() const {return NULL;} // Whether it can be used as fuel virtual bool isFuel() const {return false;} // the fuel time value virtual float getFuelTime() const {return 0.0;} // Eat, press, activate, whatever. // Called when item is right-clicked when lying on ground. // If returns true, item shall be deleted. virtual bool use(ServerEnvironment *env, Player *player){return false;} protected: u16 m_count; content_t m_content; u16 m_data; }; class MaterialItem : public InventoryItem { public: MaterialItem(content_t content, u16 count, u16 data): InventoryItem(count,data) { MapNode n(content); n = mapnode_translate_to_internal(n,SER_FMT_VER_HIGHEST); m_content = n.getContent(); } /* Implementation interface */ virtual const char* getName() const { return "MaterialItem"; } virtual void serialize(std::ostream &os) const { //os.imbue(std::locale("C")); os<<"MaterialItem3"; os<<" "; os<<(unsigned int)m_content; os<<" "; os<getContent(); u16 d = ((InventoryItem*)other)->getData(); if (c != m_content) return false; if (m_data != d) return false; return true; } u16 freeSpace() const { if (m_count > QUANTITY_ITEM_MAX_COUNT) return 0; return QUANTITY_ITEM_MAX_COUNT - m_count; } /* Other properties */ bool isCookable(uint16_t type) const; InventoryItem *createCookResult() const; bool isCrushable(CrushType type) const; InventoryItem *createCrushResult() const; virtual bool isFuel() const; virtual float getFuelTime() const; bool use(ServerEnvironment *env, Player *player); /* Special methods */ content_t getMaterial() { return m_content; } private: }; /* An item that is used as a mid-product when crafting. Subnames: - Stick */ class CraftItem : public InventoryItem { public: CraftItem(content_t content, u16 count, u16 data): InventoryItem(count,data) { m_content = content_craftitem_features(content)->content; } /* Implementation interface */ virtual const char* getName() const { return "CraftItem"; } virtual void serialize(std::ostream &os) const { os<<"CraftItem3"; os<<" "; os<<(unsigned int)m_content; os<<" "; os<getContent(); u16 d = ((InventoryItem*)other)->getData(); if (c != m_content) return false; if (m_data != d) return false; return true; } u16 freeSpace() const { if (!content_craftitem_features(m_content)->stackable) return 0; if (m_count > QUANTITY_ITEM_MAX_COUNT) return 0; return QUANTITY_ITEM_MAX_COUNT - m_count; } /* Other properties */ bool isCookable(uint16_t type) const; InventoryItem *createCookResult() const; bool isCrushable(CrushType type) const; InventoryItem *createCrushResult() const; bool isFuel() const; float getFuelTime() const; bool use(ServerEnvironment *env, Player *player); }; class ToolItem : public InventoryItem { public: ToolItem(content_t content, u16 wear, u16 data): InventoryItem(1,data) { m_wear = wear; m_content = content_toolitem_features(content).content; } /* Implementation interface */ virtual const char* getName() const { return "ToolItem"; } virtual void serialize(std::ostream &os) const { os<<"ToolItem3"; os<<" "; os<<(unsigned int)m_content; os<<" "; os<getTextureRaw(getBasename()); } #endif std::wstring getGuiName() { return narrow_to_wide(content_toolitem_features(m_content).description); } std::wstring getGuiText(); std::string getText() { return ""; } ServerActiveObject* createSAO(ServerEnvironment *env, u16 id, v3f pos); bool isCookable(uint16_t type) const; InventoryItem *createCookResult() const; bool isCrushable(CrushType type) const; InventoryItem *createCrushResult() const; bool isFuel() const; float getFuelTime() const; /* Special methods */ virtual u16 getWear() { return m_wear; } // Returns true if worn out bool addWear(u16 add) { if (m_wear < add) { m_wear = 0; return true; } m_wear -= add; return false; } virtual void setWear(u16 wear) {m_wear = wear;} private: u16 m_wear; }; class ClothesItem : public InventoryItem { public: ClothesItem(content_t content, u16 wear, u16 data): InventoryItem(1,data) { m_wear = wear; m_content = content_clothesitem_features(content)->content; } /* Implementation interface */ virtual const char* getName() const { return "ClothesItem"; } virtual void serialize(std::ostream &os) const { os<<"ClothesItem2"; os<<" "; os<<(unsigned int)m_content; os<<" "; os<texture; } video::ITexture * getImage() const; video::ITexture * getImageRaw() const { if (g_texturesource == NULL) return NULL; return g_texturesource->getTextureRaw(getBasename()); } #endif std::wstring getGuiName() { return narrow_to_wide(content_clothesitem_features(m_content)->description); } std::wstring getGuiText(); std::string getText() { return ""; } /* Special methods */ virtual u16 getWear() { return m_wear; } // Returns true if weared out bool addWear(u16 add) { if (m_wear >= 65535 - add) { m_wear = 65535; return true; }else{ m_wear += add; return false; } } virtual void setWear(u16 wear) {m_wear = wear;} private: u16 m_wear; }; class InventoryDiffData { public: InventoryDiffData(u32 i, content_t t, u16 c, u16 d): index(i), type(t), wear_count(c), data(d) { } InventoryDiffData(): index(0), type(CONTENT_IGNORE), wear_count(0), data(0) { } u32 index; content_t type; u16 wear_count; u16 data; }; class InventoryDiff { public: InventoryDiff() { clear(); } ~InventoryDiff() { clear(); } void clear() { m_data.clear(); } void add(std::string list, u32 index, content_t type, u16 count_wear, u16 data) { m_data[list][index] = InventoryDiffData(index,type,count_wear,data); } void add(std::string list, u32 index, InventoryItem *item) { if (item == NULL) { add(list,index,CONTENT_IGNORE,0,0); }else if ( (item->getContent()&CONTENT_TOOLITEM_MASK) == CONTENT_TOOLITEM_MASK || (item->getContent()&CONTENT_CLOTHESITEM_MASK) == CONTENT_CLOTHESITEM_MASK ) { add(list,index,item->getContent(),item->getWear(),item->getData()); }else{ add(list,index,item->getContent(),item->getCount(),item->getData()); } } void merge(InventoryDiff &other) { for (std::map >::iterator i = other.m_data.begin(); i != other.m_data.end(); i++) { m_data[i->first].swap(i->second); i->second.clear(); } } std::map > m_data; }; class InventoryList { public: InventoryList(std::string name, u32 size); ~InventoryList(); void clearItems(); void serialize(std::ostream &os) const; void deSerialize(std::istream &is); InventoryList(const InventoryList &other); InventoryList & operator = (const InventoryList &other); const std::string &getName() const; u32 getSize(); // Count used slots u32 getUsedSlots(); u32 getFreeSlots(); // set specific nodes only allowed in inventory void addAllowed(content_t c) {m_allowed[c] = true;} void clearAllowed() {m_allowed.clear();} // set specific nodes not allowed in inventory void addDenied(content_t c) {m_denied[c] = true;} void clearDenied() {m_denied.clear();} // whether an item is allowed in inventory bool isAllowed(content_t c) { if (m_allowed.size() > 0) return m_allowed[c]; return !m_denied[c]; } bool isAllowed(InventoryItem *item) {return isAllowed(item->getContent());} // set whether items can be stacked (more than one per slot) void setStackable(bool s=true) {m_stackable = s;} bool getStackable() {return m_stackable;} /*bool getDirty(){ return m_dirty; } void setDirty(bool dirty=true){ m_dirty = dirty; }*/ // Get pointer to item const InventoryItem * getItem(u32 i) const; InventoryItem * getItem(u32 i); // Returns old item (or NULL). Parameter can be NULL. InventoryItem * changeItem(u32 i, InventoryItem *newitem); // Delete item void deleteItem(u32 i); // Adds an item to a suitable place. Returns leftover item. // If all went into the list, returns NULL. InventoryItem * addItem(InventoryItem *newitem); // If possible, adds item to given slot. // If cannot be added at all, returns the item back. // If can be added partly, decremented item is returned back. // If can be added fully, NULL is returned. InventoryItem * addItem(u32 i, InventoryItem *newitem); // Updates item type/count/wear void updateItem(u32 i, content_t type, u16 wear_count, u16 data); // Checks whether the item could be added to the given slot bool itemFits(const u32 i, const InventoryItem *newitem); // Checks whether there is room for a given item bool roomForItem(const InventoryItem *item); // Checks whether there is room for a given item after it has been cooked bool roomForCookedItem(const InventoryItem *item); // Checks whether there is room for a given item aftr it has been crushed bool roomForCrushedItem(const InventoryItem *item); // Takes some items from a slot. // If there are not enough, takes as many as it can. // Returns NULL if couldn't take any. InventoryItem * takeItem(u32 i, u32 count); // find a stack containing an item InventoryItem *findItem(content_t item, u16 *item_i = NULL); // Decrements amount of every material item void decrementMaterials(u16 count); void print(std::ostream &o); void addDiff(u32 index, InventoryItem *item) {m_diff.add(m_name,index,item);} InventoryDiff &getDiff() {return m_diff;} private: core::array m_items; u32 m_size; std::string m_name; std::map m_allowed; std::map m_denied; bool m_stackable; InventoryDiff m_diff; }; class Inventory { public: ~Inventory(); void clear(); Inventory(); Inventory(const Inventory &other); Inventory & operator = (const Inventory &other); void serialize(std::ostream &os) const; void deSerialize(std::istream &is); InventoryList * addList(const std::string &name, u32 size); InventoryList * getList(const std::string &name); const InventoryList * getList(const std::string &name) const; bool deleteList(const std::string &name); // A shorthand for adding items. // Returns NULL if the item was fully added, leftover otherwise. InventoryItem * addItem(const std::string &listname, InventoryItem *newitem) { InventoryList *list = getList(listname); if (list == NULL) return newitem; return list->addItem(newitem); } InventoryDiff &getDiff() { m_diff.clear(); for (u32 i=0; igetDiff(); m_diff.merge(diff); } return m_diff; } private: // -1 if not found const s32 getListIndex(const std::string &name) const; core::array m_lists; InventoryDiff m_diff; }; class Player; struct InventoryContext { Player *current_player; InventoryContext(): current_player(NULL) {} }; struct InventoryAction; struct InventoryLocation; class InventoryManager { public: InventoryManager(){} virtual ~InventoryManager(){} /* Get a pointer to an inventory specified by id. id can be: - "current_player" - "nodemeta:X,Y,Z" */ virtual Inventory* getInventory(InventoryContext *c, std::string id) {return NULL;} virtual Inventory* getInventory(const InventoryLocation *loc) {return NULL;} // Used on the server by InventoryAction::apply and other stuff virtual void inventoryModified(InventoryContext *c, std::string id) {} // Used on the client virtual void inventoryAction(InventoryAction *a) {} }; #define IACTION_MOVE 0 struct InventoryAction { virtual ~InventoryAction() {} static InventoryAction * deSerialize(std::istream &is); virtual u16 getType() const = 0; virtual void serialize(std::ostream &os) const = 0; virtual void apply(InventoryContext *c, InventoryManager *mgr) = 0; }; struct IMoveAction : public InventoryAction { // count=0 means "everything" u16 count; std::string from_inv; std::string from_list; s16 from_i; std::string to_inv; std::string to_list; s16 to_i; IMoveAction() { count = 0; from_i = -1; to_i = -1; } IMoveAction(std::istream &is) { std::string ts; std::getline(is, ts, ' '); count = mystoi(ts); std::getline(is, from_inv, ' '); std::getline(is, from_list, ' '); std::getline(is, ts, ' '); from_i = mystoi(ts); std::getline(is, to_inv, ' '); std::getline(is, to_list, ' '); std::getline(is, ts, ' '); to_i = mystoi(ts); } u16 getType() const { return IACTION_MOVE; } void serialize(std::ostream &os) const { os<<"Move "; os<