1 #pragma once
2 #ifndef CATA_SRC_INVENTORY_H
3 #define CATA_SRC_INVENTORY_H
4 
5 #include <array>
6 #include <bitset>
7 #include <climits>
8 #include <cstddef>
9 #include <functional>
10 #include <iosfwd>
11 #include <limits>
12 #include <list>
13 #include <map>
14 #include <set>
15 #include <unordered_map>
16 #include <unordered_set>
17 #include <utility>
18 #include <vector>
19 
20 #include "cata_utility.h"
21 #include "item.h"
22 #include "magic_enchantment.h"
23 #include "proficiency.h"
24 #include "type_id.h"
25 #include "units_fwd.h"
26 #include "visitable.h"
27 
28 class Character;
29 class JsonIn;
30 class JsonOut;
31 class item_stack;
32 class map;
33 class npc;
34 struct tripoint;
35 
36 using invstack = std::list<std::list<item> >;
37 using invslice = std::vector<std::list<item> *>;
38 using const_invslice = std::vector<const std::list<item> *>;
39 using indexed_invslice = std::vector< std::pair<std::list<item>*, int> >;
40 using itype_bin = std::unordered_map< itype_id, std::list<const item *> >;
41 using invlets_bitset = std::bitset<std::numeric_limits<char>::max()>;
42 
43 /**
44  * Wrapper to handled a set of valid "inventory" letters. "inventory" can be any set of
45  * objects that the player can access via a single character (e.g. bionics).
46  * The class is (currently) derived from std::string for compatibility and because it's
47  * simpler. But it may be changed to derive from `std::set<int>` or similar to get the full
48  * range of possible characters.
49  */
50 class invlet_wrapper : private std::string
51 {
52     public:
invlet_wrapper(const char * chars)53         explicit invlet_wrapper( const char *chars ) : std::string( chars ) { }
54 
55         bool valid( int invlet ) const;
get_allowed_chars()56         std::string get_allowed_chars() const {
57             return *this;
58         }
59 
60         using std::string::begin;
61         using std::string::end;
62         using std::string::rbegin;
63         using std::string::rend;
64         using std::string::size;
65         using std::string::length;
66 };
67 
68 const extern invlet_wrapper inv_chars;
69 
70 // For each item id, store a set of "favorite" inventory letters.
71 // This class maintains a bidirectional mapping between invlet letters and item ids.
72 // Each invlet has at most one id and each id has any number of invlets.
73 class invlet_favorites
74 {
75     public:
76         invlet_favorites() = default;
77         explicit invlet_favorites( const std::unordered_map<itype_id, std::string> & );
78 
79         void set( char invlet, const itype_id & );
80         void erase( char invlet );
81         bool contains( char invlet, const itype_id & ) const;
82         std::string invlets_for( const itype_id & ) const;
83 
84         // For serialization only
85         const std::unordered_map<itype_id, std::string> &get_invlets_by_id() const;
86     private:
87         std::unordered_map<itype_id, std::string> invlets_by_id;
88         std::array<itype_id, 256> ids_by_invlet;
89 };
90 
91 class inventory : public visitable
92 {
93     public:
94 
95         invslice slice();
96         const_invslice const_slice() const;
97         const std::list<item> &const_stack( int i ) const;
98         size_t size() const;
99 
100         std::map<char, itype_id> assigned_invlet;
101 
102         inventory();
103         inventory( inventory && ) noexcept = default;
104         inventory( const inventory & ) = default;
105         inventory &operator=( inventory && ) = default;
106         inventory &operator=( const inventory & ) = default;
107 
108         inventory &operator+= ( const inventory &rhs );
109         inventory &operator+= ( const item &rhs );
110         inventory &operator+= ( const std::list<item> &rhs );
111         inventory &operator+= ( const std::vector<item> &rhs );
112         inventory &operator+= ( const item_stack &rhs );
113         inventory  operator+ ( const inventory &rhs );
114         inventory  operator+ ( const item &rhs );
115         inventory  operator+ ( const std::list<item> &rhs );
116 
117         void unsort(); // flags the inventory as unsorted
118         void clear();
119         void push_back( const std::list<item> &newits );
120         // returns a reference to the added item
121         item &add_item( item newit, bool keep_invlet = false, bool assign_invlet = true,
122                         bool should_stack = true );
123         void add_item_keep_invlet( const item &newit );
124         void push_back( const item &newit );
125 
126         // used by form_from_map, if tool was already provisioned returns nullptr
127         item *provide_pseudo_item( const itype_id &id, int battery );
128 
129         /* Check all items for proper stacking, rearranging as needed
130          * game pointer is not necessary, but if supplied, will ensure no overlap with
131          * the player's worn items / weapon
132          */
133         void restack( Character &p );
134         void form_from_zone( map &m, std::unordered_set<tripoint> &zone_pts, const Character *pl = nullptr,
135                              bool assign_invlet = true );
136         void form_from_map( const tripoint &origin, int range, const Character *pl = nullptr,
137                             bool assign_invlet = true,
138                             bool clear_path = true );
139         void form_from_map( map &m, const tripoint &origin, int range, const Character *pl = nullptr,
140                             bool assign_invlet = true,
141                             bool clear_path = true );
142         void form_from_map( map &m, std::vector<tripoint> pts, const Character *pl,
143                             bool assign_invlet = true );
144         /**
145          * Remove a specific item from the inventory. The item is compared
146          * by pointer. Contents of the item are removed as well.
147          * @param it A pointer to the item to be removed. The item *must* exists
148          * in this inventory.
149          * @return A copy of the removed item.
150          */
151         item remove_item( const item *it );
152         item remove_item( int position );
153         /**
154          * Randomly select items until the volume quota is filled.
155          */
156         std::list<item> remove_randomly_by_volume( const units::volume &volume );
157         std::list<item> reduce_stack( int position, int quantity );
158 
159         const item &find_item( int position ) const;
160         item &find_item( int position );
161 
162         /**
163          * Returns the item position of the stack that contains the given item (compared by
164          * pointers). Returns INT_MIN if the item is not found.
165          * Note that this may lose some information, for example the returned position is the
166          * same when the given item points to the container and when it points to the item inside
167          * the container. All items that are part of the same stack have the same item position.
168          */
169         int position_by_item( const item *it ) const;
170         int position_by_type( const itype_id &type ) const;
171 
172         /** Return the item position of the item with given invlet, return INT_MIN if
173          * the inventory does not have such an item with that invlet. Don't use this on npcs inventory. */
174         int invlet_to_position( char invlet ) const;
175 
176         // Below, "amount" refers to quantity
177         //        "charges" refers to charges
178         std::list<item> use_amount( const itype_id &it, int quantity,
179                                     const std::function<bool( const item & )> &filter = return_true<item> );
180 
181         int leak_level( const flag_id &flag ) const; // level of leaked bad stuff from items
182 
183         // NPC/AI functions
184         int worst_item_value( npc *p ) const;
185         bool has_enough_painkiller( int pain ) const;
186         item *most_appropriate_painkiller( int pain );
187 
188         void rust_iron_items();
189 
190         units::mass weight() const;
191         units::mass weight_without( const std::map<const item *, int> & ) const;
192         units::volume volume() const;
193         units::volume volume_without( const std::map<const item *, int> & ) const;
194 
195         // dumps contents into dest (does not delete contents)
196         void dump( std::vector<item *> &dest );
197 
198         // vector rather than list because it's NOT an item stack
199         // returns all items that need processing
200         std::vector<item *> active_items();
201 
202         void json_load_invcache( JsonIn &jsin );
203         void json_load_items( JsonIn &jsin );
204 
205         void json_save_invcache( JsonOut &json ) const;
206         void json_save_items( JsonOut &json ) const;
207 
208         // Assigns an invlet if any remain.  If none do, will assign ` if force is
209         // true, empty (invlet = 0) otherwise.
210         void assign_empty_invlet( item &it, const Character &p, bool force = false );
211         // Assigns the item with the given invlet, and updates the favorite invlet cache. Does not check for uniqueness
212         void reassign_item( item &it, char invlet, bool remove_old = true );
213         // Removes invalid invlets, and assigns new ones if assign_invlet is true. Does not update the invlet cache.
214         void update_invlet( item &it, bool assign_invlet = true );
215 
216         invlets_bitset allocated_invlets() const;
217 
218         /**
219          * Returns visitable items binned by their itype.
220          * May not contain items that wouldn't be visited by @ref visitable methods.
221          */
222         const itype_bin &get_binned_items() const;
223 
224         void update_cache_with_item( item &newit );
225 
226         void copy_invlet_of( const inventory &other );
227 
228         // gets a singular enchantment that is an amalgamation of all items that have active enchantments
229         enchantment get_active_enchantment_cache( const Character &owner ) const;
230 
231         int count_item( const itype_id &item_type ) const;
232 
233         book_proficiency_bonuses get_book_proficiency_bonuses() const;
234 
235         // inherited from `visitable`
236         bool has_quality( const quality_id &qual, int level = 1, int qty = 1 ) const override;
237         VisitResponse visit_items( const std::function<VisitResponse( item *, item * )> &func ) const
238         override;
239         std::list<item> remove_items_with( const std::function<bool( const item & )> &filter,
240                                            int count = INT_MAX ) override;
241         int charges_of( const itype_id &what, int limit = INT_MAX,
242                         const std::function<bool( const item & )> &filter = return_true<item>,
243                         const std::function<void( int )> &visitor = nullptr ) const override;
244         int amount_of( const itype_id &what, bool pseudo = true,
245                        int limit = INT_MAX,
246                        const std::function<bool( const item & )> &filter = return_true<item> ) const override;
247 
248     private:
249         invlet_favorites invlet_cache;
250         char find_usable_cached_invlet( const itype_id &item_type );
251 
252         invstack items;
253 
254         // tracker for provide_pseudo_item to prevent duplicate tools/liquids
255         std::set<itype_id> provisioned_pseudo_tools;
256 
257         mutable bool binned = false;
258         /**
259          * Items binned by their type.
260          * That is, item_bin["carrot"] is a list of pointers to all carrots in inventory.
261          * `mutable` because this is a pure cache that doesn't affect the contained items.
262          */
263         mutable itype_bin binned_items;
264 };
265 
266 #endif // CATA_SRC_INVENTORY_H
267