1 /*
2  * inventory.c
3  * Copyright (C) 2009-2018 Joachim de Groot <jdegroot@web.de>
4  *
5  * NLarn is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * NLarn is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <glib.h>
20 
21 #include "amulets.h"
22 #include "game.h"
23 #include "inventory.h"
24 #include "items.h"
25 #include "extdefs.h"
26 #include "potions.h"
27 
28 /* functions */
29 
inv_new(gconstpointer owner)30 inventory *inv_new(gconstpointer owner)
31 {
32     inventory *ninv;
33 
34     ninv = g_malloc0(sizeof(inventory));
35     ninv->content = g_ptr_array_new();
36 
37     ninv->owner = owner;
38 
39     return ninv;
40 }
41 
inv_destroy(inventory * inv,gboolean special)42 void inv_destroy(inventory *inv, gboolean special)
43 {
44     g_assert(inv != NULL);
45 
46     while (inv_length(inv) > 0)
47     {
48         item *it = inv_get(inv, inv_length(inv) - 1);
49 
50         if (special)
51         {
52             /* Mark potion of cure dianthroritis and eye of larn
53                as never having been created. */
54             if (it->type == IT_POTION && it->id == PO_CURE_DIANTHR)
55                 nlarn->cure_dianthr_created = FALSE;
56             else if (it->type == IT_AMULET && it->id == AM_LARN)
57                 nlarn->amulet_created[AM_LARN] = FALSE;
58         }
59         g_ptr_array_remove(inv->content, it->oid);
60         item_destroy(it);
61     }
62 
63     g_ptr_array_free(inv->content, TRUE);
64 
65     g_free(inv);
66 }
67 
inv_serialize(inventory * inv)68 cJSON *inv_serialize(inventory *inv)
69 {
70     cJSON *sinv = cJSON_CreateArray();
71 
72     for (guint idx = 0; idx < inv_length(inv); idx++)
73     {
74         item *it = inv_get(inv, idx);
75         cJSON_AddItemToArray(sinv, cJSON_CreateNumber(GPOINTER_TO_UINT(it->oid)));
76     }
77 
78     return sinv;
79 }
80 
inv_deserialize(cJSON * iser)81 inventory *inv_deserialize(cJSON *iser)
82 {
83     inventory *inv = g_malloc0(sizeof(inventory));
84     inv->content = g_ptr_array_new();
85 
86     for (int idx = 0; idx < cJSON_GetArraySize(iser); idx++)
87     {
88         guint oid = cJSON_GetArrayItem(iser, idx)->valueint;
89         g_ptr_array_add(inv->content, GUINT_TO_POINTER(oid));
90     }
91 
92     return inv;
93 }
94 
inv_callbacks_set(inventory * inv,inv_callback_bool pre_add,inv_callback_void post_add,inv_callback_bool pre_del,inv_callback_void post_del)95 void inv_callbacks_set(inventory *inv, inv_callback_bool pre_add,
96                        inv_callback_void post_add, inv_callback_bool pre_del,
97                        inv_callback_void post_del)
98 {
99     g_assert (inv != NULL);
100 
101     inv->pre_add  = pre_add;
102     inv->post_add = post_add;
103     inv->pre_del  = pre_del;
104     inv->post_del = post_del;
105 }
106 
inv_add(inventory ** inv,item * it)107 int inv_add(inventory **inv, item *it)
108 {
109     g_assert(inv != NULL && it != NULL && it->oid != NULL);
110 
111     /* create inventory if necessary */
112     if (!(*inv))
113     {
114         *inv = inv_new(NULL);
115     }
116 
117     /* check if pre_add callback is set */
118     if ((*inv)->pre_add)
119     {
120         /* call pre_add callback */
121         if (!(*inv)->pre_add(*inv, it))
122         {
123             return FALSE;
124         }
125     }
126 
127     /* stack stackable items */
128     if (item_is_stackable(it->type))
129     {
130         /* loop through items in the target inventory to find a similar item */
131         for (guint idx = 0; idx < inv_length(*inv); idx++)
132         {
133             item *i = inv_get(*inv, idx);
134             /* compare the current item with the one which is to be added */
135             if (item_compare(i, it))
136             {
137                 /* just increase item count and release the original */
138                 i->count += it->count;
139                 item_destroy(it);
140 
141                 it = NULL;
142 
143                 break;
144             }
145         }
146     }
147 
148     if (it != NULL)
149     {
150         /* add the item to the inventory if it has not already been added */
151         g_ptr_array_add((*inv)->content, it->oid);
152     }
153 
154     /* call post_add callback */
155     if ((*inv)->post_add)
156     {
157         (*inv)->post_add(*inv, it);
158     }
159 
160     return inv_length(*inv);
161 }
162 
inv_get(inventory * inv,guint idx)163 item *inv_get(inventory *inv, guint idx)
164 {
165     g_assert (inv != NULL && idx < inv->content->len);
166     gpointer oid = g_ptr_array_index(inv->content, idx);
167 
168     return game_item_get(nlarn, oid);
169 }
170 
inv_del(inventory ** inv,guint idx)171 item *inv_del(inventory **inv, guint idx)
172 {
173     item *itm;
174 
175     g_assert(*inv != NULL && (*inv)->content != NULL && idx < inv_length(*inv));
176 
177     itm = inv_get(*inv, idx);
178 
179     if ((*inv)->pre_del)
180     {
181         if (!(*inv)->pre_del(*inv, itm))
182         {
183             return NULL;
184         }
185     }
186 
187     g_ptr_array_remove_index((*inv)->content, idx);
188 
189     if ((*inv)->post_del)
190     {
191         (*inv)->post_del(*inv, itm);
192     }
193 
194     /* destroy inventory if empty and not owned by anybody */
195     if (!inv_length(*inv) && !(*inv)->owner)
196     {
197         inv_destroy(*inv, FALSE);
198         *inv = NULL;
199     }
200 
201     return itm;
202 }
203 
inv_del_element(inventory ** inv,item * it)204 int inv_del_element(inventory **inv, item *it)
205 {
206     g_assert(*inv != NULL && (*inv)->content != NULL && it != NULL);
207 
208     if ((*inv)->pre_del)
209     {
210         if (!(*inv)->pre_del(*inv, it))
211         {
212             return FALSE;
213         }
214     }
215 
216     g_ptr_array_remove((*inv)->content, it->oid);
217 
218     if ((*inv)->post_del)
219     {
220         (*inv)->post_del(*inv, it);
221     }
222 
223     /* destroy inventory if empty and not owned by anybody */
224     if (!inv_length(*inv) && !(*inv)->owner)
225     {
226         inv_destroy(*inv, FALSE);
227         *inv = NULL;
228     }
229 
230     return TRUE;
231 }
232 
inv_del_oid(inventory ** inv,gpointer oid)233 int inv_del_oid(inventory **inv, gpointer oid)
234 {
235     g_assert(*inv != NULL && (*inv)->content != NULL && oid != NULL);
236 
237     if (!g_ptr_array_remove((*inv)->content, oid))
238     {
239         return FALSE;
240     }
241 
242     /* destroy inventory if empty and not owned by anybody */
243     if (!inv_length(*inv) && !(*inv)->owner)
244     {
245         inv_destroy(*inv, FALSE);
246         *inv = NULL;
247     }
248 
249     return TRUE;
250 }
251 
inv_erode(inventory ** inv,item_erosion_type iet,gboolean visible,int (* ifilter)(item *))252 void inv_erode(inventory **inv, item_erosion_type iet,
253                gboolean visible, int (*ifilter)(item *))
254 {
255     g_assert(inv != NULL);
256 
257     for (guint idx = 0; idx < inv_length(*inv); idx++)
258     {
259         item *it = inv_get(*inv, idx);
260 
261         /*
262          * If no filter was given, erode all items, otherwise
263          * those which are agreed on by the filter function.
264          */
265         if (ifilter == NULL || ifilter(it))
266         {
267             item_erode(inv, it, iet, visible);
268         }
269     }
270 }
271 
inv_length(inventory * inv)272 guint inv_length(inventory *inv)
273 {
274     return (inv == NULL) ? 0 : inv->content->len;
275 }
276 
inv_sort(inventory * inv,GCompareDataFunc compare_func,gpointer user_data)277 void inv_sort(inventory *inv, GCompareDataFunc compare_func, gpointer user_data)
278 {
279     g_assert(inv != NULL && inv->content != NULL);
280 
281     g_ptr_array_sort_with_data(inv->content, compare_func, user_data);
282 }
283 
inv_weight(inventory * inv)284 int inv_weight(inventory *inv)
285 {
286     int sum = 0;
287 
288     if (inv == NULL)
289     {
290         return 0;
291     }
292 
293     /* add contents weight */
294     for (guint idx = 0; idx < inv_length(inv); idx++)
295     {
296         sum += item_weight(inv_get(inv, idx));
297     }
298 
299     return sum;
300 }
301 
inv_length_filtered(inventory * inv,int (* ifilter)(item *))302 guint inv_length_filtered(inventory *inv, int (*ifilter)(item *))
303 {
304     int count = 0;
305 
306     if (inv == NULL)
307     {
308         /* check for non-existent inventories */
309         return 0;
310     }
311 
312     /* return the inventory length if no filter has been set */
313     if (!ifilter)
314     {
315         return inv_length(inv);
316     }
317 
318     for (guint pos = 0; pos < inv_length(inv); pos++)
319     {
320         item *i = inv_get(inv, pos);
321 
322         if (ifilter(i))
323         {
324             count++;
325         }
326     }
327 
328     return count;
329 }
330 
inv_get_filtered(inventory * inv,guint idx,int (* ifilter)(item *))331 item *inv_get_filtered(inventory *inv, guint idx, int (*ifilter)(item *))
332 {
333     guint curr = 0;
334 
335     /* return the inventory length if no filter has been set */
336     if (!ifilter)
337     {
338         return inv_get(inv, idx);
339     }
340 
341     for (guint num = 0; num < inv_length(inv); num++)
342     {
343         item *i = inv_get(inv, num);
344 
345         if (ifilter(i))
346         {
347             /* filter matches */
348             if (curr == idx)
349             {
350                 /* this is the requested item */
351                 return i;
352             }
353             else
354             {
355                 curr++;
356             }
357         }
358     }
359 
360     /* not found */
361     return NULL;
362 }
363