1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9 
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 Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser 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.
18 */
19 
20 #pragma once
21 
22 #include "itemdef.h"
23 #include "irrlichttypes.h"
24 #include "itemstackmetadata.h"
25 #include <istream>
26 #include <ostream>
27 #include <string>
28 #include <vector>
29 #include <cassert>
30 
31 struct ToolCapabilities;
32 
33 struct ItemStack
34 {
35 	ItemStack() = default;
36 
37 	ItemStack(const std::string &name_, u16 count_,
38 			u16 wear, IItemDefManager *itemdef);
39 
40 	~ItemStack() = default;
41 
42 	// Serialization
43 	void serialize(std::ostream &os, bool serialize_meta = true) const;
44 	// Deserialization. Pass itemdef unless you don't want aliases resolved.
45 	void deSerialize(std::istream &is, IItemDefManager *itemdef = NULL);
46 	void deSerialize(const std::string &s, IItemDefManager *itemdef = NULL);
47 
48 	// Returns the string used for inventory
49 	std::string getItemString(bool include_meta = true) const;
50 	// Returns the tooltip
51 	std::string getDescription(IItemDefManager *itemdef) const;
52 	std::string getShortDescription(IItemDefManager *itemdef) const;
53 
54 	/*
55 		Quantity methods
56 	*/
57 
emptyItemStack58 	bool empty() const
59 	{
60 		return count == 0;
61 	}
62 
clearItemStack63 	void clear()
64 	{
65 		name = "";
66 		count = 0;
67 		wear = 0;
68 		metadata.clear();
69 	}
70 
addItemStack71 	void add(u16 n)
72 	{
73 		count += n;
74 	}
75 
removeItemStack76 	void remove(u16 n)
77 	{
78 		assert(count >= n); // Pre-condition
79 		count -= n;
80 		if(count == 0)
81 			clear(); // reset name, wear and metadata too
82 	}
83 
84 	// Maximum size of a stack
getStackMaxItemStack85 	u16 getStackMax(IItemDefManager *itemdef) const
86 	{
87 		return itemdef->get(name).stack_max;
88 	}
89 
90 	// Number of items that can be added to this stack
freeSpaceItemStack91 	u16 freeSpace(IItemDefManager *itemdef) const
92 	{
93 		u16 max = getStackMax(itemdef);
94 		if (count >= max)
95 			return 0;
96 		return max - count;
97 	}
98 
99 	// Returns false if item is not known and cannot be used
isKnownItemStack100 	bool isKnown(IItemDefManager *itemdef) const
101 	{
102 		return itemdef->isKnown(name);
103 	}
104 
105 	// Returns a pointer to the item definition struct,
106 	// or a fallback one (name="unknown") if the item is unknown.
getDefinitionItemStack107 	const ItemDefinition& getDefinition(
108 			IItemDefManager *itemdef) const
109 	{
110 		return itemdef->get(name);
111 	}
112 
113 	// Get tool digging properties, or those of the hand if not a tool
getToolCapabilitiesItemStack114 	const ToolCapabilities& getToolCapabilities(
115 			IItemDefManager *itemdef) const
116 	{
117 		const ToolCapabilities *item_cap =
118 			itemdef->get(name).tool_capabilities;
119 
120 		if (item_cap == NULL)
121 			// Fall back to the hand's tool capabilities
122 			item_cap = itemdef->get("").tool_capabilities;
123 
124 		assert(item_cap != NULL);
125 		return metadata.getToolCapabilities(*item_cap); // Check for override
126 	}
127 
128 	// Wear out (only tools)
129 	// Returns true if the item is (was) a tool
addWearItemStack130 	bool addWear(s32 amount, IItemDefManager *itemdef)
131 	{
132 		if(getDefinition(itemdef).type == ITEM_TOOL)
133 		{
134 			if(amount > 65535 - wear)
135 				clear();
136 			else if(amount < -wear)
137 				wear = 0;
138 			else
139 				wear += amount;
140 			return true;
141 		}
142 
143 		return false;
144 	}
145 
146 	// If possible, adds newitem to this item.
147 	// If cannot be added at all, returns the item back.
148 	// If can be added partly, decremented item is returned back.
149 	// If can be added fully, empty item is returned.
150 	ItemStack addItem(ItemStack newitem, IItemDefManager *itemdef);
151 
152 	// Checks whether newitem could be added.
153 	// If restitem is non-NULL, it receives the part of newitem that
154 	// would be left over after adding.
155 	bool itemFits(ItemStack newitem,
156 			ItemStack *restitem,  // may be NULL
157 			IItemDefManager *itemdef) const;
158 
159 	// Takes some items.
160 	// If there are not enough, takes as many as it can.
161 	// Returns empty item if couldn't take any.
162 	ItemStack takeItem(u32 takecount);
163 
164 	// Similar to takeItem, but keeps this ItemStack intact.
165 	ItemStack peekItem(u32 peekcount) const;
166 
167 	bool operator ==(const ItemStack &s) const
168 	{
169 		return (this->name     == s.name &&
170 				this->count    == s.count &&
171 				this->wear     == s.wear &&
172 				this->metadata == s.metadata);
173 	}
174 
175 	bool operator !=(const ItemStack &s) const
176 	{
177 		return !(*this == s);
178 	}
179 
180 	/*
181 		Properties
182 	*/
183 	std::string name = "";
184 	u16 count = 0;
185 	u16 wear = 0;
186 	ItemStackMetadata metadata;
187 };
188 
189 class InventoryList
190 {
191 public:
192 	InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef);
193 	~InventoryList() = default;
194 	void clearItems();
195 	void setSize(u32 newsize);
196 	void setWidth(u32 newWidth);
197 	void setName(const std::string &name);
198 	void serialize(std::ostream &os, bool incremental) const;
199 	void deSerialize(std::istream &is);
200 
201 	InventoryList(const InventoryList &other);
202 	InventoryList & operator = (const InventoryList &other);
203 	bool operator == (const InventoryList &other) const;
204 	bool operator != (const InventoryList &other) const
205 	{
206 		return !(*this == other);
207 	}
208 
209 	const std::string &getName() const;
210 	u32 getSize() const;
211 	u32 getWidth() const;
212 	// Count used slots
213 	u32 getUsedSlots() const;
214 	u32 getFreeSlots() const;
215 
216 	// Get reference to item
217 	const ItemStack& getItem(u32 i) const;
218 	ItemStack& getItem(u32 i);
219 	// Returns old item. Parameter can be an empty item.
220 	ItemStack changeItem(u32 i, const ItemStack &newitem);
221 	// Delete item
222 	void deleteItem(u32 i);
223 
224 	// Adds an item to a suitable place. Returns leftover item (possibly empty).
225 	ItemStack addItem(const ItemStack &newitem);
226 
227 	// If possible, adds item to given slot.
228 	// If cannot be added at all, returns the item back.
229 	// If can be added partly, decremented item is returned back.
230 	// If can be added fully, empty item is returned.
231 	ItemStack addItem(u32 i, const ItemStack &newitem);
232 
233 	// Checks whether the item could be added to the given slot
234 	// If restitem is non-NULL, it receives the part of newitem that
235 	// would be left over after adding.
236 	bool itemFits(const u32 i, const ItemStack &newitem,
237 			ItemStack *restitem = NULL) const;
238 
239 	// Checks whether there is room for a given item
240 	bool roomForItem(const ItemStack &item) const;
241 
242 	// Checks whether the given count of the given item
243 	// exists in this inventory list.
244 	// If match_meta is false, only the items' names are compared.
245 	bool containsItem(const ItemStack &item, bool match_meta) const;
246 
247 	// Removes the given count of the given item name from
248 	// this inventory list. Walks the list in reverse order.
249 	// If not as many items exist as requested, removes as
250 	// many as possible.
251 	// Returns the items that were actually removed.
252 	ItemStack removeItem(const ItemStack &item);
253 
254 	// Takes some items from a slot.
255 	// If there are not enough, takes as many as it can.
256 	// Returns empty item if couldn't take any.
257 	ItemStack takeItem(u32 i, u32 takecount);
258 
259 	// Move an item to a different list (or a different stack in the same list)
260 	// count is the maximum number of items to move (0 for everything)
261 	// returns number of moved items
262 	u32 moveItem(u32 i, InventoryList *dest, u32 dest_i,
263 		u32 count = 0, bool swap_if_needed = true, bool *did_swap = NULL);
264 
265 	// like moveItem, but without a fixed destination index
266 	// also with optional rollback recording
267 	void moveItemSomewhere(u32 i, InventoryList *dest, u32 count);
268 
checkModified()269 	inline bool checkModified() const { return m_dirty; }
270 	inline void setModified(bool dirty = true) { m_dirty = dirty; }
271 
272 private:
273 	std::vector<ItemStack> m_items;
274 	std::string m_name;
275 	u32 m_size;
276 	u32 m_width = 0;
277 	IItemDefManager *m_itemdef;
278 	bool m_dirty = true;
279 };
280 
281 class Inventory
282 {
283 public:
284 	~Inventory();
285 
286 	void clear();
287 
288 	Inventory(IItemDefManager *itemdef);
289 	Inventory(const Inventory &other);
290 	Inventory & operator = (const Inventory &other);
291 	bool operator == (const Inventory &other) const;
292 	bool operator != (const Inventory &other) const
293 	{
294 		return !(*this == other);
295 	}
296 
297 	// Never ever serialize to disk using "incremental"!
298 	void serialize(std::ostream &os, bool incremental = false) const;
299 	void deSerialize(std::istream &is);
300 
301 	InventoryList * addList(const std::string &name, u32 size);
302 	InventoryList * getList(const std::string &name);
303 	const InventoryList * getList(const std::string &name) const;
304 	std::vector<const InventoryList*> getLists();
305 	bool deleteList(const std::string &name);
306 	// A shorthand for adding items. Returns leftover item (possibly empty).
addItem(const std::string & listname,const ItemStack & newitem)307 	ItemStack addItem(const std::string &listname, const ItemStack &newitem)
308 	{
309 		InventoryList *list = getList(listname);
310 		if(list == NULL)
311 			return newitem;
312 		return list->addItem(newitem);
313 	}
314 
checkModified()315 	inline bool checkModified() const
316 	{
317 		if (m_dirty)
318 			return true;
319 
320 		for (const auto &list : m_lists)
321 			if (list->checkModified())
322 				return true;
323 
324 		return false;
325 	}
326 
327 	inline void setModified(bool dirty = true)
328 	{
329 		m_dirty = dirty;
330 		// Set all as handled
331 		if (!dirty) {
332 			for (const auto &list : m_lists)
333 				list->setModified(dirty);
334 		}
335 	}
336 private:
337 	// -1 if not found
338 	const s32 getListIndex(const std::string &name) const;
339 
340 	std::vector<InventoryList*> m_lists;
341 	IItemDefManager *m_itemdef;
342 	bool m_dirty = true;
343 };
344