File: | game/inventory.cpp |
Location: | line 27, column 5 |
Description: | Access to field 'next' results in a dereference of a null pointer (loaded from variable 'prev') |
1 | #include "inventory.h" | ||
2 | |||
3 | static inline void I_Free (inventoryInterface_t* self, void *data) | ||
4 | { | ||
5 | self->import->Free(data); | ||
6 | } | ||
7 | |||
8 | static inline void *I_Alloc (inventoryInterface_t* self, size_t size) | ||
9 | { | ||
10 | return self->import->Alloc(size); | ||
11 | } | ||
12 | |||
13 | static void I_RemoveInvList (inventoryInterface_t* self, invList_t *invList) | ||
14 | { | ||
15 | Com_DPrintf(DEBUG_SHARED0x02, "I_RemoveInvList: remove one slot (%s)\n", self->name); | ||
16 | |||
17 | /* first entry */ | ||
18 | if (self->invList == invList) { | ||
| |||
19 | invList_t *ic = self->invList; | ||
20 | self->invList = ic->next; | ||
21 | I_Free(self, ic); | ||
22 | } else { | ||
23 | invList_t *ic = self->invList; | ||
24 | invList_t* prev = NULL__null; | ||
25 | while (ic) { | ||
| |||
26 | if (ic == invList) { | ||
| |||
27 | prev->next = ic->next; | ||
| |||
28 | I_Free(self, ic); | ||
29 | break; | ||
30 | } | ||
31 | prev = ic; | ||
32 | ic = ic->next; | ||
33 | } | ||
34 | } | ||
35 | } | ||
36 | |||
37 | static invList_t* I_AddInvList (inventoryInterface_t* self, invList_t **invList) | ||
38 | { | ||
39 | invList_t *newEntry; | ||
40 | invList_t *list; | ||
41 | |||
42 | Com_DPrintf(DEBUG_SHARED0x02, "I_AddInvList: add one slot (%s)\n", self->name); | ||
43 | |||
44 | /* create the list */ | ||
45 | if (!*invList) { | ||
46 | *invList = (invList_t*)I_Alloc(self, sizeof(**invList)); | ||
47 | (*invList)->next = NULL__null; /* not really needed - but for better readability */ | ||
48 | return *invList; | ||
49 | } else | ||
50 | list = *invList; | ||
51 | |||
52 | while (list->next) | ||
53 | list = list->next; | ||
54 | |||
55 | newEntry = (invList_t*)I_Alloc(self, sizeof(*newEntry)); | ||
56 | list->next = newEntry; | ||
57 | newEntry->next = NULL__null; /* not really needed - but for better readability */ | ||
58 | |||
59 | return newEntry; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * @brief Add an item to a specified container in a given inventory. | ||
64 | * @note Set x and y to NONE if the item should get added to an automatically chosen free spot in the container. | ||
65 | * @param[in] self The inventory interface pointer | ||
66 | * @param[in,out] inv Pointer to inventory definition, to which we will add item. | ||
67 | * @param[in] item Item to add to given container (needs to have "rotated" tag already set/checked, this is NOT checked here!) | ||
68 | * @param[in] container Container in given inventory definition, where the new item will be stored. | ||
69 | * @param[in] x The x location in the container. | ||
70 | * @param[in] y The x location in the container. | ||
71 | * @param[in] amount How many items of this type should be added. (this will overwrite the amount as defined in "item.amount") | ||
72 | * @sa I_RemoveFromInventory | ||
73 | * @return the @c invList_t pointer the item was added to, or @c NULL in case of an error (item wasn't added) | ||
74 | */ | ||
75 | static invList_t *I_AddToInventory (inventoryInterface_t* self, inventory_t * const inv, const item_t* const item, const invDef_t * container, int x, int y, int amount) | ||
76 | { | ||
77 | invList_t *ic; | ||
78 | int checkedTo; | ||
79 | |||
80 | if (!item->t) | ||
81 | return NULL__null; | ||
82 | |||
83 | if (amount <= 0) | ||
84 | return NULL__null; | ||
85 | |||
86 | assert(inv)(__builtin_expect(!(inv), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 86, "inv") : (void)0); | ||
87 | assert(container)(__builtin_expect(!(container), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 87, "container") : (void)0); | ||
88 | |||
89 | if (container->single && inv->c[container->id] && inv->c[container->id]->next) | ||
90 | return NULL__null; | ||
91 | |||
92 | /* idEquip and idFloor */ | ||
93 | if (container->temp) { | ||
94 | for (ic = inv->c[container->id]; ic; ic = ic->next) | ||
95 | if (INVSH_CompareItem(&ic->item, item)) { | ||
96 | ic->item.amount += amount; | ||
97 | Com_DPrintf(DEBUG_SHARED0x02, "I_AddToInventory: Amount of '%s': %i (%s)\n", | ||
98 | ic->item.t->name, ic->item.amount, self->name); | ||
99 | return ic; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | if (x < 0 || y < 0 || x >= SHAPE_BIG_MAX_WIDTH32 || y >= SHAPE_BIG_MAX_HEIGHT16) { | ||
104 | /* No (sane) position in container given as parameter - find free space on our own. */ | ||
105 | INVSH_FindSpace(inv, item, container, &x, &y, NULL__null); | ||
106 | if (x == NONE-1) | ||
107 | return NULL__null; | ||
108 | } | ||
109 | |||
110 | checkedTo = INVSH_CheckToInventory(inv, item->t, container, x, y, NULL__null); | ||
111 | assert(checkedTo)(__builtin_expect(!(checkedTo), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 111, "checkedTo") : (void)0); | ||
112 | |||
113 | /* not found - add a new one */ | ||
114 | ic = I_AddInvList(self, &inv->c[container->id]); | ||
115 | |||
116 | /* Set the data in the new entry to the data we got via function-parameters.*/ | ||
117 | ic->item = *item; | ||
118 | ic->item.amount = amount; | ||
119 | |||
120 | /* don't reset an already applied rotation */ | ||
121 | if (checkedTo == INV_FITS_ONLY_ROTATED) | ||
122 | ic->item.rotated = true; | ||
123 | ic->x = x; | ||
124 | ic->y = y; | ||
125 | |||
126 | return ic; | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * @param[in] self The inventory interface pointer | ||
131 | * @param[in] i The inventory the container is in. | ||
132 | * @param[in] container The container where the item should be removed. | ||
133 | * @param[in] fItem The item to be removed. | ||
134 | * @return true If removal was successful. | ||
135 | * @return false If nothing was removed or an error occurred. | ||
136 | * @sa I_AddToInventory | ||
137 | */ | ||
138 | static bool I_RemoveFromInventory (inventoryInterface_t* self, inventory_t* const i, const invDef_t * container, invList_t *fItem) | ||
139 | { | ||
140 | invList_t *ic, *previous; | ||
141 | |||
142 | assert(i)(__builtin_expect(!(i), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 142, "i") : (void)0); | ||
143 | assert(container)(__builtin_expect(!(container), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 143, "container") : (void)0); | ||
144 | assert(fItem)(__builtin_expect(!(fItem), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 144, "fItem") : (void)0); | ||
145 | |||
146 | ic = i->c[container->id]; | ||
147 | if (!ic) | ||
| |||
148 | return false; | ||
149 | |||
150 | /** @todo the problem here is, that in case of a move inside the same container | ||
151 | * the item don't just get updated x and y values but it is tried to remove | ||
152 | * one of the items => crap - maybe we have to change the inventory move function | ||
153 | * to check for this case of move and only update the x and y coordinates instead | ||
154 | * of calling the add and remove functions */ | ||
155 | if (container->single || ic == fItem) { | ||
| |||
156 | self->cacheItem = ic->item; | ||
157 | /* temp container like idEquip and idFloor */ | ||
158 | if (container->temp && ic->item.amount > 1) { | ||
159 | ic->item.amount--; | ||
160 | Com_DPrintf(DEBUG_SHARED0x02, "I_RemoveFromInventory: Amount of '%s': %i (%s)\n", | ||
161 | ic->item.t->name, ic->item.amount, self->name); | ||
162 | return true; | ||
163 | } | ||
164 | |||
165 | if (container->single && ic->next) | ||
166 | Com_Printf("I_RemoveFromInventory: Error: single container %s has many items. (%s)\n", container->name, self->name); | ||
167 | |||
168 | /* An item in other containers than idFloor or idEquip should | ||
169 | * always have an amount value of 1. | ||
170 | * The other container types do not support stacking.*/ | ||
171 | assert(ic->item.amount == 1)(__builtin_expect(!(ic->item.amount == 1), 0) ? __assert_rtn (__func__, "src/game/inventory.cpp", 171, "ic->item.amount == 1" ) : (void)0); | ||
172 | |||
173 | i->c[container->id] = ic->next; | ||
174 | |||
175 | /* updated invUnused to be able to reuse this space later again */ | ||
176 | I_RemoveInvList(self, ic); | ||
177 | |||
178 | return true; | ||
179 | } | ||
180 | |||
181 | for (previous = i->c[container->id]; ic; ic = ic->next) { | ||
| |||
| |||
| |||
| |||
182 | if (ic == fItem) { | ||
| |||
| |||
| |||
| |||
183 | self->cacheItem = ic->item; | ||
184 | /* temp container like idEquip and idFloor */ | ||
185 | if (ic->item.amount > 1 && container->temp) { | ||
| |||
186 | ic->item.amount--; | ||
187 | Com_DPrintf(DEBUG_SHARED0x02, "I_RemoveFromInventory: Amount of '%s': %i (%s)\n", | ||
188 | ic->item.t->name, ic->item.amount, self->name); | ||
189 | return true; | ||
190 | } | ||
191 | |||
192 | if (ic == i->c[container->id]) | ||
| |||
193 | i->c[container->id] = i->c[container->id]->next; | ||
194 | else | ||
195 | previous->next = ic->next; | ||
196 | |||
197 | I_RemoveInvList(self, ic); | ||
| |||
198 | |||
199 | return true; | ||
200 | } | ||
201 | previous = ic; | ||
202 | } | ||
203 | return false; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * @brief Conditions for moving items between containers. | ||
208 | * @param[in] self The inventory interface pointer | ||
209 | * @param[in] inv Inventory to move in. | ||
210 | * @param[in] from Source container. | ||
211 | * @param[in] fItem The item to be moved. | ||
212 | * @param[in] to Destination container. | ||
213 | * @param[in] tx X coordinate in destination container. | ||
214 | * @param[in] ty Y coordinate in destination container. | ||
215 | * @param[in,out] TU pointer to entity available TU at this moment | ||
216 | * or @c NULL if TU doesn't matter (outside battlescape) | ||
217 | * @param[out] icp | ||
218 | * @return IA_NOTIME when not enough TU. | ||
219 | * @return IA_NONE if no action possible. | ||
220 | * @return IA_NORELOAD if you cannot reload a weapon. | ||
221 | * @return IA_RELOAD_SWAP in case of exchange of ammo in a weapon. | ||
222 | * @return IA_RELOAD when reloading. | ||
223 | * @return IA_ARMOUR when placing an armour on the actor. | ||
224 | * @return IA_MOVE when just moving an item. | ||
225 | */ | ||
226 | static inventory_action_t I_MoveInInventory (inventoryInterface_t* self, inventory_t* const inv, const invDef_t * from, invList_t *fItem, const invDef_t * to, int tx, int ty, int *TU, invList_t ** icp) | ||
227 | { | ||
228 | invList_t *ic; | ||
229 | |||
230 | int time; | ||
231 | int checkedTo = INV_DOES_NOT_FIT; | ||
232 | bool alreadyRemovedSource = false; | ||
233 | |||
234 | assert(to)(__builtin_expect(!(to), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 234, "to") : (void)0); | ||
235 | assert(from)(__builtin_expect(!(from), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 235, "from") : (void)0); | ||
236 | |||
237 | if (icp) | ||
238 | *icp = NULL__null; | ||
239 | |||
240 | if (from == to && fItem->x == tx && fItem->y == ty) | ||
241 | return IA_NONE; | ||
242 | |||
243 | time = from->out + to->in; | ||
244 | if (from == to) { | ||
245 | if (INV_IsFloorDef(from)) | ||
246 | time = 0; | ||
247 | else | ||
248 | time /= 2; | ||
249 | } | ||
250 | |||
251 | if (TU && *TU < time) | ||
252 | return IA_NOTIME; | ||
253 | |||
254 | assert(inv)(__builtin_expect(!(inv), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 254, "inv") : (void)0); | ||
255 | |||
256 | /* Special case for moving an item within the same container. */ | ||
257 | if (from == to) { | ||
258 | /* Do nothing if we move inside a scroll container. */ | ||
259 | if (from->scroll) | ||
260 | return IA_NONE; | ||
261 | |||
262 | ic = inv->c[from->id]; | ||
263 | for (; ic; ic = ic->next) { | ||
264 | if (ic == fItem) { | ||
265 | if (ic->item.amount > 1) { | ||
266 | checkedTo = INVSH_CheckToInventory(inv, ic->item.t, to, tx, ty, fItem); | ||
267 | if (checkedTo & INV_FITS) { | ||
268 | ic->x = tx; | ||
269 | ic->y = ty; | ||
270 | if (icp) | ||
271 | *icp = ic; | ||
272 | return IA_MOVE; | ||
273 | } | ||
274 | return IA_NONE; | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* If weapon is twohanded and is moved from hand to hand do nothing. */ | ||
281 | /* Twohanded weapon are only in CSI->idRight. */ | ||
282 | if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to) && INV_IsRightDef(from)) { | ||
283 | return IA_NONE; | ||
284 | } | ||
285 | |||
286 | /* If non-armour moved to an armour slot then abort. | ||
287 | * Same for non extension items when moved to an extension slot. */ | ||
288 | if ((to->armour && !INV_IsArmour(fItem->item.t)((strcmp((fItem->item.t)->type, "armour") == 0))) | ||
289 | || (to->extension && !fItem->item.t->extension) | ||
290 | || (to->headgear && !fItem->item.t->headgear)) { | ||
291 | return IA_NONE; | ||
292 | } | ||
293 | |||
294 | /* Check if the target is a blocked inv-armour and source!=dest. */ | ||
295 | if (to->single) | ||
296 | checkedTo = INVSH_CheckToInventory(inv, fItem->item.t, to, 0, 0, fItem); | ||
297 | else { | ||
298 | if (tx == NONE-1 || ty == NONE-1) | ||
299 | INVSH_FindSpace(inv, &fItem->item, to, &tx, &ty, fItem); | ||
300 | /* still no valid location found */ | ||
301 | if (tx == NONE-1 || ty == NONE-1) | ||
302 | return IA_NONE; | ||
303 | |||
304 | checkedTo = INVSH_CheckToInventory(inv, fItem->item.t, to, tx, ty, fItem); | ||
305 | } | ||
306 | |||
307 | if (to->armour && from != to && !checkedTo) { | ||
308 | item_t cacheItem2; | ||
309 | invList_t *icTo; | ||
310 | /* Store x/y origin coordinates of removed (source) item. | ||
311 | * When we re-add it we can use this. */ | ||
312 | const int cacheFromX = fItem->x; | ||
313 | const int cacheFromY = fItem->y; | ||
314 | |||
315 | /* Check if destination/blocking item is the same as source/from item. | ||
316 | * In that case the move is not needed -> abort. */ | ||
317 | icTo = INVSH_SearchInInventory(inv, to, tx, ty); | ||
318 | if (fItem->item.t == icTo->item.t) | ||
319 | return IA_NONE; | ||
320 | |||
321 | /* Actually remove the ammo from the 'from' container. */ | ||
322 | if (!self->RemoveFromInventory(self, inv, from, fItem)) | ||
323 | return IA_NONE; | ||
324 | else | ||
325 | /* Removal successful - store this info. */ | ||
326 | alreadyRemovedSource = true; | ||
327 | |||
328 | cacheItem2 = self->cacheItem; /* Save/cache (source) item. The cacheItem is modified in I_MoveInInventory. */ | ||
329 | |||
330 | /* Move the destination item to the source. */ | ||
331 | self->MoveInInventory(self, inv, to, icTo, from, cacheFromX, cacheFromY, TU, icp); | ||
332 | |||
333 | /* Reset the cached item (source) (It'll be move to container emptied by destination item later.) */ | ||
334 | self->cacheItem = cacheItem2; | ||
335 | } else if (!checkedTo) { | ||
336 | /* Get the target-invlist (e.g. a weapon). We don't need to check for | ||
337 | * scroll because checkedTo is always true here. */ | ||
338 | ic = INVSH_SearchInInventory(inv, to, tx, ty); | ||
339 | |||
340 | if (ic && !INV_IsEquipDef(to) && INVSH_LoadableInWeapon(fItem->item.t, ic->item.t)) { | ||
341 | /* A target-item was found and the dragged item (implicitly ammo) | ||
342 | * can be loaded in it (implicitly weapon). */ | ||
343 | if (ic->item.a >= ic->item.t->ammo && ic->item.m == fItem->item.t) { | ||
344 | /* Weapon already fully loaded with the same ammunition -> abort */ | ||
345 | return IA_NORELOAD; | ||
346 | } | ||
347 | time += ic->item.t->reload; | ||
348 | if (!TU || *TU >= time) { | ||
349 | if (TU) | ||
350 | *TU -= time; | ||
351 | if (ic->item.a >= ic->item.t->ammo) { | ||
352 | /* exchange ammo */ | ||
353 | const item_t item = {NONE_AMMO0, NULL__null, ic->item.m, 0, 0}; | ||
354 | /* Put current ammo in place of the new ammo unless floor - there can be more than 1 item */ | ||
355 | const int cacheFromX = INV_IsFloorDef(from) ? NONE-1 : fItem->x; | ||
356 | const int cacheFromY = INV_IsFloorDef(from) ? NONE-1 : fItem->y; | ||
357 | |||
358 | /* Actually remove the ammo from the 'from' container. */ | ||
359 | if (!self->RemoveFromInventory(self, inv, from, fItem)) | ||
360 | return IA_NONE; | ||
361 | |||
362 | /* Add the currently used ammo in place of the new ammo in the "from" container. */ | ||
363 | if (self->AddToInventory(self, inv, &item, from, cacheFromX, cacheFromY, 1) == NULL__null) | ||
364 | Sys_Error("Could not reload the weapon - add to inventory failed (%s)", self->name); | ||
365 | |||
366 | ic->item.m = self->cacheItem.t; | ||
367 | if (icp) | ||
368 | *icp = ic; | ||
369 | return IA_RELOAD_SWAP; | ||
370 | } else { | ||
371 | /* Actually remove the ammo from the 'from' container. */ | ||
372 | if (!self->RemoveFromInventory(self, inv, from, fItem)) | ||
373 | return IA_NONE; | ||
374 | |||
375 | ic->item.m = self->cacheItem.t; | ||
376 | /* loose ammo of type ic->item.m saved on server side */ | ||
377 | ic->item.a = ic->item.t->ammo; | ||
378 | if (icp) | ||
379 | *icp = ic; | ||
380 | return IA_RELOAD; | ||
381 | } | ||
382 | } | ||
383 | /* Not enough time -> abort. */ | ||
384 | return IA_NOTIME; | ||
385 | } | ||
386 | |||
387 | /* temp container like idEquip and idFloor */ | ||
388 | if (ic && to->temp) { | ||
389 | /* We are moving to a blocked location container but it's the base-equipment floor or a battlescape floor. | ||
390 | * We add the item anyway but it'll not be displayed (yet) | ||
391 | * This is then used in I_AddToInventory below.*/ | ||
392 | /** @todo change the other code to browse trough these things. */ | ||
393 | INVSH_FindSpace(inv, &fItem->item, to, &tx, &ty, fItem); | ||
394 | if (tx == NONE-1 || ty == NONE-1) { | ||
395 | Com_DPrintf(DEBUG_SHARED0x02, "I_MoveInInventory - item will be added non-visible (%s)\n", self->name); | ||
396 | } | ||
397 | } else { | ||
398 | /* Impossible move -> abort. */ | ||
399 | return IA_NONE; | ||
400 | } | ||
401 | } | ||
402 | |||
403 | /* twohanded exception - only CSI->idRight is allowed for fireTwoHanded weapons */ | ||
404 | if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to)) | ||
405 | to = &self->csi->ids[self->csi->idRight]; | ||
406 | |||
407 | if (checkedTo == INV_FITS_ONLY_ROTATED) { | ||
408 | /* Set rotated tag */ | ||
409 | fItem->item.rotated = true; | ||
410 | } else if (fItem->item.rotated) { | ||
411 | /* Remove rotated tag */ | ||
412 | fItem->item.rotated = false; | ||
413 | } | ||
414 | |||
415 | /* Actually remove the item from the 'from' container (if it wasn't already removed). */ | ||
416 | if (!alreadyRemovedSource) | ||
417 | if (!self->RemoveFromInventory(self, inv, from, fItem)) | ||
418 | return IA_NONE; | ||
419 | |||
420 | /* successful */ | ||
421 | if (TU) | ||
422 | *TU -= time; | ||
423 | |||
424 | assert(self->cacheItem.t)(__builtin_expect(!(self->cacheItem.t), 0) ? __assert_rtn( __func__, "src/game/inventory.cpp", 424, "self->cacheItem.t" ) : (void)0); | ||
425 | ic = self->AddToInventory(self, inv, &self->cacheItem, to, tx, ty, 1); | ||
426 | |||
427 | /* return data */ | ||
428 | if (icp) { | ||
429 | assert(ic)(__builtin_expect(!(ic), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 429, "ic") : (void)0); | ||
430 | *icp = ic; | ||
431 | } | ||
432 | |||
433 | if (INV_IsArmourDef(to)) { | ||
434 | assert(INV_IsArmour(self->cacheItem.t))(__builtin_expect(!(((strcmp((self->cacheItem.t)->type, "armour") == 0))), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 434, "INV_IsArmour(self->cacheItem.t)") : (void)0); | ||
435 | return IA_ARMOUR; | ||
436 | } else | ||
437 | return IA_MOVE; | ||
438 | } | ||
439 | |||
440 | /** | ||
441 | * @brief Tries to add an item to a container (in the inventory inv). | ||
442 | * @param[in] self The inventory interface pointer | ||
443 | * @param[in] inv Inventory pointer to add the item. | ||
444 | * @param[in] item Item to add to inventory. | ||
445 | * @param[in] container Container id. | ||
446 | * @sa INVSH_FindSpace | ||
447 | * @sa I_AddToInventory | ||
448 | */ | ||
449 | static bool I_TryAddToInventory (inventoryInterface_t* self, inventory_t* const inv, const item_t * const item, const invDef_t * container) | ||
450 | { | ||
451 | int x, y; | ||
452 | |||
453 | INVSH_FindSpace(inv, item, container, &x, &y, NULL__null); | ||
454 | |||
455 | if (x == NONE-1) { | ||
456 | assert(y == NONE)(__builtin_expect(!(y == -1), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 456, "y == NONE") : (void)0); | ||
457 | return false; | ||
458 | } else { | ||
459 | const int checkedTo = INVSH_CheckToInventory(inv, item->t, container, x, y, NULL__null); | ||
460 | if (!checkedTo) | ||
461 | return false; | ||
462 | else { | ||
463 | item_t itemRotation = *item; | ||
464 | if (checkedTo == INV_FITS_ONLY_ROTATED) | ||
465 | itemRotation.rotated = true; | ||
466 | else | ||
467 | itemRotation.rotated = false; | ||
468 | |||
469 | return self->AddToInventory(self, inv, &itemRotation, container, x, y, 1) != NULL__null; | ||
470 | } | ||
471 | } | ||
472 | } | ||
473 | |||
474 | /** | ||
475 | * @brief Clears the linked list of a container - removes all items from this container. | ||
476 | * @param[in] self The inventory interface pointer | ||
477 | * @param[in] i The inventory where the container is located. | ||
478 | * @param[in] container Index of the container which will be cleared. | ||
479 | * @sa I_DestroyInventory | ||
480 | * @note This should only be called for temp containers if the container is really a temp container | ||
481 | * e.g. the container of a dropped weapon in tactical mission (ET_ITEM) | ||
482 | * in every other case just set the pointer to NULL for a temp container like idEquip or idFloor | ||
483 | */ | ||
484 | static void I_EmptyContainer (inventoryInterface_t* self, inventory_t* const i, const invDef_t * container) | ||
485 | { | ||
486 | invList_t *ic; | ||
487 | |||
488 | ic = i->c[container->id]; | ||
489 | |||
490 | while (ic) { | ||
491 | invList_t *old = ic; | ||
492 | ic = ic->next; | ||
493 | I_RemoveInvList(self, old); | ||
494 | } | ||
495 | |||
496 | i->c[container->id] = NULL__null; | ||
497 | } | ||
498 | |||
499 | /** | ||
500 | * @brief Destroys inventory. | ||
501 | * @param[in] self The inventory interface pointer | ||
502 | * @param[in] inv Pointer to the inventory which should be erased. | ||
503 | * @note Loops through all containers in inventory. @c NULL for temp containers are skipped, | ||
504 | * for real containers @c I_EmptyContainer is called. | ||
505 | * @sa I_EmptyContainer | ||
506 | */ | ||
507 | static void I_DestroyInventory (inventoryInterface_t* self, inventory_t* const inv) | ||
508 | { | ||
509 | containerIndex_t container; | ||
510 | |||
511 | if (!inv) | ||
512 | return; | ||
513 | |||
514 | for (container = 0; container < self->csi->numIDs; container++) { | ||
515 | const invDef_t *invDef = &self->csi->ids[container]; | ||
516 | if (!invDef->temp) | ||
517 | self->EmptyContainer(self, inv, invDef); | ||
518 | } | ||
519 | |||
520 | OBJZERO(*inv)(memset(&((*inv)), (0), sizeof((*inv)))); | ||
521 | } | ||
522 | |||
523 | |||
524 | #define WEAPONLESS_BONUS0.4 0.4 /* if you got neither primary nor secondary weapon, this is the chance to retry to get one (before trying to get grenades or blades) */ | ||
525 | |||
526 | /** | ||
527 | * @brief Pack a weapon, possibly with some ammo | ||
528 | * @param[in] self The inventory interface pointer | ||
529 | * @param[in] inv The inventory that will get the weapon | ||
530 | * @param[in] weapon The weapon type index in gi.csi->ods | ||
531 | * @param[in] ed The equipment for debug messages | ||
532 | * @param[in] missedPrimary if actor didn't get primary weapon, this is 0-100 number to increase ammo number. | ||
533 | * @sa INVSH_LoadableInWeapon | ||
534 | */ | ||
535 | static int I_PackAmmoAndWeapon (inventoryInterface_t *self, inventory_t* const inv, const objDef_t* weapon, int missedPrimary, const equipDef_t *ed) | ||
536 | { | ||
537 | const objDef_t *ammo = NULL__null; | ||
538 | item_t item = {NONE_AMMO0, NULL__null, NULL__null, 0, 0}; | ||
539 | bool allowLeft; | ||
540 | bool packed; | ||
541 | int ammoMult = 1; | ||
542 | |||
543 | assert(!INV_IsArmour(weapon))(__builtin_expect(!(!((strcmp((weapon)->type, "armour") == 0))), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp", 543 , "!INV_IsArmour(weapon)") : (void)0); | ||
544 | item.t = weapon; | ||
545 | |||
546 | /* are we going to allow trying the left hand */ | ||
547 | allowLeft = !(inv->c[self->csi->idRight] && inv->c[self->csi->idRight]->item.t->fireTwoHanded); | ||
548 | |||
549 | if (weapon->oneshot) { | ||
550 | /* The weapon provides its own ammo (i.e. it is charged or loaded in the base.) */ | ||
551 | item.a = weapon->ammo; | ||
552 | item.m = weapon; | ||
553 | Com_DPrintf(DEBUG_SHARED0x02, "I_PackAmmoAndWeapon: oneshot weapon '%s' in equipment '%s' (%s).\n", | ||
554 | weapon->id, ed->id, self->name); | ||
555 | } else if (!weapon->reload) { | ||
556 | item.m = item.t; /* no ammo needed, so fire definitions are in t */ | ||
557 | } else { | ||
558 | /* find some suitable ammo for the weapon (we will have at least one if there are ammos for this | ||
559 | * weapon in equipment definition) */ | ||
560 | int totalAvailableAmmo = 0; | ||
561 | int i; | ||
562 | for (i = 0; i < self->csi->numODs; i++) { | ||
563 | const objDef_t *obj = INVSH_GetItemByIDX(i); | ||
564 | if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) { | ||
565 | totalAvailableAmmo++; | ||
566 | } | ||
567 | } | ||
568 | if (totalAvailableAmmo) { | ||
569 | int randNumber = rand() % totalAvailableAmmo; | ||
570 | for (i = 0; i < self->csi->numODs; i++) { | ||
571 | const objDef_t *obj = INVSH_GetItemByIDX(i); | ||
572 | if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) { | ||
573 | randNumber--; | ||
574 | if (randNumber < 0) { | ||
575 | ammo = obj; | ||
576 | break; | ||
577 | } | ||
578 | } | ||
579 | } | ||
580 | } | ||
581 | |||
582 | if (!ammo) { | ||
583 | Com_DPrintf(DEBUG_SHARED0x02, "I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n", | ||
584 | weapon->id, ed->id, self->name); | ||
585 | return 0; | ||
586 | } | ||
587 | /* load ammo */ | ||
588 | item.a = weapon->ammo; | ||
589 | item.m = ammo; | ||
590 | } | ||
591 | |||
592 | if (!item.m) { | ||
593 | Com_Printf("I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n", | ||
594 | weapon->id, ed->id, self->name); | ||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | /* now try to pack the weapon */ | ||
599 | packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idRight]); | ||
600 | if (packed) | ||
601 | ammoMult = 3; | ||
602 | if (!packed && allowLeft) | ||
603 | packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idLeft]); | ||
604 | if (!packed) | ||
605 | packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idBelt]); | ||
606 | if (!packed) | ||
607 | packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idHolster]); | ||
608 | if (!packed) | ||
609 | return 0; | ||
610 | |||
611 | |||
612 | /* pack some more ammo in the backpack */ | ||
613 | if (ammo) { | ||
614 | int num; | ||
615 | int numpacked = 0; | ||
616 | |||
617 | /* how many clips? */ | ||
618 | num = (1 + ed->numItems[ammo->idx]) | ||
619 | * (float) (1.0f + missedPrimary / 100.0); | ||
620 | |||
621 | /* pack some ammo */ | ||
622 | while (num--) { | ||
623 | item_t mun = {NONE_AMMO0, NULL__null, NULL__null, 0, 0}; | ||
624 | |||
625 | mun.t = ammo; | ||
626 | /* ammo to backpack; belt is for knives and grenades */ | ||
627 | numpacked += self->TryAddToInventory(self, inv, &mun, &self->csi->ids[self->csi->idBackpack]); | ||
628 | /* no problem if no space left; one ammo already loaded */ | ||
629 | if (numpacked > ammoMult || numpacked * weapon->ammo > 11) | ||
630 | break; | ||
631 | } | ||
632 | } | ||
633 | |||
634 | return true; | ||
635 | } | ||
636 | |||
637 | |||
638 | /** | ||
639 | * @brief Equip melee actor with item defined per teamDefs. | ||
640 | * @param[in] self The inventory interface pointer | ||
641 | * @param[in] inv The inventory that will get the weapon. | ||
642 | * @param[in] td Pointer to a team definition. | ||
643 | * @note Weapons assigned here cannot be collected in any case. These are dummy "actor weapons". | ||
644 | */ | ||
645 | static void I_EquipActorMelee (inventoryInterface_t *self, inventory_t* const inv, const teamDef_t* td) | ||
646 | { | ||
647 | const objDef_t *obj; | ||
648 | item_t item; | ||
649 | |||
650 | assert(td->onlyWeapon)(__builtin_expect(!(td->onlyWeapon), 0) ? __assert_rtn(__func__ , "src/game/inventory.cpp", 650, "td->onlyWeapon") : (void )0); | ||
651 | |||
652 | /* Get weapon */ | ||
653 | obj = td->onlyWeapon; | ||
654 | |||
655 | /* Prepare item. This kind of item has no ammo, fire definitions are in item.t. */ | ||
656 | item.t = obj; | ||
657 | item.m = item.t; | ||
658 | item.a = NONE_AMMO0; | ||
659 | /* Every melee actor weapon definition is firetwohanded, add to right hand. */ | ||
660 | if (!obj->fireTwoHanded) | ||
661 | Sys_Error("INVSH_EquipActorMelee: melee weapon %s for team %s is not firetwohanded! (%s)\n", | ||
662 | obj->id, td->id, self->name); | ||
663 | self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idRight]); | ||
664 | } | ||
665 | |||
666 | /** | ||
667 | * @brief Equip robot actor with default weapon. (defined in ugv_t->weapon) | ||
668 | * @param[in] self The inventory interface pointer | ||
669 | * @param[in] inv The inventory that will get the weapon. | ||
670 | * @param[in] weapon Pointer to the item which being added to robot's inventory. | ||
671 | */ | ||
672 | static void I_EquipActorRobot (inventoryInterface_t *self, inventory_t* const inv, const objDef_t* weapon) | ||
673 | { | ||
674 | item_t item; | ||
675 | |||
676 | assert(weapon)(__builtin_expect(!(weapon), 0) ? __assert_rtn(__func__, "src/game/inventory.cpp" , 676, "weapon") : (void)0); | ||
677 | |||
678 | /* Prepare weapon in item. */ | ||
679 | item.t = weapon; | ||
680 | item.a = NONE_AMMO0; | ||
681 | |||
682 | /* Get ammo for item/weapon. */ | ||
683 | assert(weapon->numAmmos > 0)(__builtin_expect(!(weapon->numAmmos > 0), 0) ? __assert_rtn (__func__, "src/game/inventory.cpp", 683, "weapon->numAmmos > 0" ) : (void)0); /* There _has_ to be at least one ammo-type. */ | ||
684 | assert(weapon->ammos[0])(__builtin_expect(!(weapon->ammos[0]), 0) ? __assert_rtn(__func__ , "src/game/inventory.cpp", 684, "weapon->ammos[0]") : (void )0); | ||
685 | item.m = weapon->ammos[0]; | ||
686 | |||
687 | self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idRight]); | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * @brief Types of weapon that can be selected | ||
692 | */ | ||
693 | typedef enum { | ||
694 | WEAPON_PARTICLE_OR_NORMAL = 0, /**< primary weapon is a particle or normal weapon */ | ||
695 | WEAPON_OTHER = 1, /**< primary weapon is not a particle or normal weapon */ | ||
696 | WEAPON_NO_PRIMARY = 2 /**< no primary weapon */ | ||
697 | } equipPrimaryWeaponType_t; | ||
698 | |||
699 | /** | ||
700 | * @brief Fully equip one actor. The equipment that is added to the inventory of the given actor | ||
701 | * is taken from the equipment script definition. | ||
702 | * @param[in] self The inventory interface pointer | ||
703 | * @param[in] inv The inventory that will get the weapon. | ||
704 | * @param[in] ed The equipment that is added from to the actors inventory | ||
705 | * @param[in] td Pointer to teamdef data - to get the weapon and armour bools. | ||
706 | * @note The code below is a complete implementation | ||
707 | * of the scheme sketched at the beginning of equipment_missions.ufo. | ||
708 | * Beware: If two weapons in the same category have the same price, | ||
709 | * only one will be considered for inventory. | ||
710 | */ | ||
711 | static void I_EquipActor (inventoryInterface_t* self, inventory_t* const inv, const equipDef_t *ed, const teamDef_t* td) | ||
712 | { | ||
713 | int i; | ||
714 | const int numEquip = lengthof(ed->numItems)(sizeof(ed->numItems) / sizeof(*(ed->numItems))); | ||
715 | int repeat = 0; | ||
716 | const float AKIMBO_CHANCE = 0.3; /**< if you got a one-handed secondary weapon (and no primary weapon), | ||
717 | this is the chance to get another one (between 0 and 1) */ | ||
718 | |||
719 | if (td->weapons) { | ||
720 | equipPrimaryWeaponType_t primary = WEAPON_NO_PRIMARY; | ||
721 | int sum; | ||
722 | int missedPrimary = 0; /**< If actor has a primary weapon, this is zero. Otherwise, this is the probability * 100 | ||
723 | * that the actor had to get a primary weapon (used to compensate the lack of primary weapon) */ | ||
724 | const objDef_t *primaryWeapon = NULL__null; | ||
725 | int hasWeapon = 0; | ||
726 | /* Primary weapons */ | ||
727 | const int maxWeaponIdx = std::min(self->csi->numODs - 1, numEquip - 1); | ||
728 | int randNumber = rand() % 100; | ||
729 | for (i = 0; i < maxWeaponIdx; i++) { | ||
730 | const objDef_t *obj = INVSH_GetItemByIDX(i); | ||
731 | if (ed->numItems[i] && obj->weapon && obj->fireTwoHanded && obj->isPrimary) { | ||
732 | randNumber -= ed->numItems[i]; | ||
733 | missedPrimary += ed->numItems[i]; | ||
734 | if (!primaryWeapon && randNumber < 0) | ||
735 | primaryWeapon = obj; | ||
736 | } | ||
737 | } | ||
738 | /* See if a weapon has been selected. */ | ||
739 | if (primaryWeapon) { | ||
740 | hasWeapon += I_PackAmmoAndWeapon(self, inv, primaryWeapon, 0, ed); | ||
741 | if (hasWeapon) { | ||
742 | int ammo; | ||
743 | |||
744 | /* Find the first possible ammo to check damage type. */ | ||
745 | for (ammo = 0; ammo < self->csi->numODs; ammo++) | ||
746 | if (ed->numItems[ammo] && INVSH_LoadableInWeapon(&self->csi->ods[ammo], primaryWeapon)) | ||
747 | break; | ||
748 | if (ammo < self->csi->numODs) { | ||
749 | if (/* To avoid two particle weapons. */ | ||
750 | !(self->csi->ods[ammo].dmgtype == self->csi->damParticle) | ||
751 | /* To avoid SMG + Assault Rifle */ | ||
752 | && !(self->csi->ods[ammo].dmgtype == self->csi->damNormal)) { | ||
753 | primary = WEAPON_OTHER; | ||
754 | } else { | ||
755 | primary = WEAPON_PARTICLE_OR_NORMAL; | ||
756 | } | ||
757 | } | ||
758 | /* reset missedPrimary: we got a primary weapon */ | ||
759 | missedPrimary = 0; | ||
760 | } else { | ||
761 | Com_DPrintf(DEBUG_SHARED0x02, "INVSH_EquipActor: primary weapon '%s' couldn't be equipped in equipment '%s' (%s).\n", | ||
762 | primaryWeapon->id, ed->id, self->name); | ||
763 | repeat = WEAPONLESS_BONUS0.4 > frand(); | ||
764 | } | ||
765 | } | ||
766 | |||
767 | /* Sidearms (secondary weapons with reload). */ | ||
768 | do { | ||
769 | int randNumber = rand() % 100; | ||
770 | const objDef_t *secondaryWeapon = NULL__null; | ||
771 | for (i = 0; i < self->csi->numODs; i++) { | ||
772 | const objDef_t *obj = INVSH_GetItemByIDX(i); | ||
773 | if (ed->numItems[i] && obj->weapon && obj->reload && !obj->deplete && obj->isSecondary) { | ||
774 | randNumber -= ed->numItems[i] / (primary == WEAPON_PARTICLE_OR_NORMAL ? 2 : 1); | ||
775 | if (randNumber < 0) { | ||
776 | secondaryWeapon = obj; | ||
777 | break; | ||
778 | } | ||
779 | } | ||
780 | } | ||
781 | |||
782 | if (secondaryWeapon) { | ||
783 | hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, missedPrimary, ed); | ||
784 | if (hasWeapon) { | ||
785 | /* Try to get the second akimbo pistol if no primary weapon. */ | ||
786 | if (primary == WEAPON_NO_PRIMARY && !secondaryWeapon->fireTwoHanded && frand() < AKIMBO_CHANCE) { | ||
787 | I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed); | ||
788 | } | ||
789 | } | ||
790 | } | ||
791 | } while (!hasWeapon && repeat--); | ||
792 | |||
793 | /* Misc items and secondary weapons without reload. */ | ||
794 | if (!hasWeapon) | ||
795 | repeat = WEAPONLESS_BONUS0.4 > frand(); | ||
796 | else | ||
797 | repeat = 0; | ||
798 | /* Misc object probability can be bigger than 100 -- you're sure to | ||
799 | * have one misc if it fits your backpack */ | ||
800 | sum = 0; | ||
801 | for (i = 0; i < self->csi->numODs; i++) { | ||
802 | const objDef_t *obj = INVSH_GetItemByIDX(i); | ||
803 | if (ed->numItems[i] && ((obj->weapon && obj->isSecondary | ||
804 | && (!obj->reload || obj->deplete)) || obj->isMisc)) { | ||
805 | /* if ed->num[i] is greater than 100, the first number is the number of items you'll get: | ||
806 | * don't take it into account for probability | ||
807 | * Make sure that the probability is at least one if an item can be selected */ | ||
808 | sum += ed->numItems[i] ? std::max(ed->numItems[i] % 100, 1) : 0; | ||
809 | } | ||
810 | } | ||
811 | if (sum) { | ||
812 | do { | ||
813 | int randNumber = rand() % sum; | ||
814 | const objDef_t *secondaryWeapon = NULL__null; | ||
815 | for (i = 0; i < self->csi->numODs; i++) { | ||
816 | const objDef_t *obj = INVSH_GetItemByIDX(i); | ||
817 | if (ed->numItems[i] && ((obj->weapon && obj->isSecondary | ||
818 | && (!obj->reload || obj->deplete)) || obj->isMisc)) { | ||
819 | randNumber -= ed->numItems[i] ? std::max(ed->numItems[i] % 100, 1) : 0; | ||
820 | if (randNumber < 0) { | ||
821 | secondaryWeapon = obj; | ||
822 | break; | ||
823 | } | ||
824 | } | ||
825 | } | ||
826 | |||
827 | if (secondaryWeapon) { | ||
828 | int num = ed->numItems[secondaryWeapon->idx] / 100 + (ed->numItems[secondaryWeapon->idx] % 100 >= 100 * frand()); | ||
829 | while (num--) { | ||
830 | hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed); | ||
831 | } | ||
832 | } | ||
833 | } while (repeat--); /* Gives more if no serious weapons. */ | ||
834 | } | ||
835 | |||
836 | /* If no weapon at all, bad guys will always find a blade to wield. */ | ||
837 | if (!hasWeapon) { | ||
838 | int maxPrice = 0; | ||
839 | const objDef_t *blade = NULL__null; | ||
840 | Com_DPrintf(DEBUG_SHARED0x02, "INVSH_EquipActor: no weapon picked in equipment '%s', defaulting to the most expensive secondary weapon without reload. (%s)\n", | ||
841 | ed->id, self->name); | ||
842 | for (i = 0; i < self->csi->numODs; i++) { | ||
843 | const objDef_t *obj = INVSH_GetItemByIDX(i); | ||
844 | if (ed->numItems[i] && obj->weapon && obj->isSecondary && !obj->reload) { | ||
845 | if (obj->price > maxPrice) { | ||
846 | maxPrice = obj->price; | ||
847 | blade = obj; | ||
848 | } | ||
849 | } | ||
850 | } | ||
851 | if (maxPrice) | ||
852 | hasWeapon += I_PackAmmoAndWeapon(self, inv, blade, 0, ed); | ||
853 | } | ||
854 | /* If still no weapon, something is broken, or no blades in equipment. */ | ||
855 | if (!hasWeapon) | ||
856 | Com_DPrintf(DEBUG_SHARED0x02, "INVSH_EquipActor: cannot add any weapon; no secondary weapon without reload detected for equipment '%s' (%s).\n", | ||
857 | ed->id, self->name); | ||
858 | |||
859 | /* Armour; especially for those without primary weapons. */ | ||
860 | repeat = (float) missedPrimary > frand() * 100.0; | ||
861 | } else { | ||
862 | return; | ||
863 | } | ||
864 | |||
865 | if (td->armour) { | ||
866 | do { | ||
867 | int randNumber = rand() % 100; | ||
868 | for (i = 0; i < self->csi->numODs; i++) { | ||
869 | const objDef_t *armour = INVSH_GetItemByIDX(i); | ||
870 | if (ed->numItems[i] && INV_IsArmour(armour)((strcmp((armour)->type, "armour") == 0))) { | ||
871 | randNumber -= ed->numItems[i]; | ||
872 | if (randNumber < 0) { | ||
873 | const item_t item = {NONE_AMMO0, NULL__null, armour, 0, 0}; | ||
874 | if (self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idArmour])) { | ||
875 | repeat = 0; | ||
876 | break; | ||
877 | } | ||
878 | } | ||
879 | } | ||
880 | } | ||
881 | } while (repeat-- > 0); | ||
882 | } else { | ||
883 | Com_DPrintf(DEBUG_SHARED0x02, "INVSH_EquipActor: teamdef '%s' may not carry armour (%s)\n", | ||
884 | td->name, self->name); | ||
885 | } | ||
886 | |||
887 | { | ||
888 | int randNumber = rand() % 10; | ||
889 | for (i = 0; i < self->csi->numODs; i++) { | ||
890 | if (ed->numItems[i]) { | ||
891 | const objDef_t *miscItem = INVSH_GetItemByIDX(i); | ||
892 | if (miscItem->isMisc && !miscItem->weapon) { | ||
893 | randNumber -= ed->numItems[i]; | ||
894 | if (randNumber < 0) { | ||
895 | const bool oneShot = miscItem->oneshot; | ||
896 | const item_t item = {oneShot ? miscItem->ammo : NONE_AMMO0, oneShot ? miscItem : NULL__null, miscItem, 0, 0}; | ||
897 | containerIndex_t container; | ||
898 | if (miscItem->headgear) | ||
899 | container = self->csi->idHeadgear; | ||
900 | else if (miscItem->extension) | ||
901 | container = self->csi->idExtension; | ||
902 | else | ||
903 | container = self->csi->idBackpack; | ||
904 | self->TryAddToInventory(self, inv, &item, &self->csi->ids[container]); | ||
905 | } | ||
906 | } | ||
907 | } | ||
908 | } | ||
909 | } | ||
910 | } | ||
911 | |||
912 | /** | ||
913 | * @brief Calculate the number of used inventory slots | ||
914 | * @param[in] self The inventory interface pointer | ||
915 | * @return The number of free inventory slots | ||
916 | */ | ||
917 | static int I_GetUsedSlots (inventoryInterface_t* self) | ||
918 | { | ||
919 | int i = 0; | ||
920 | const invList_t* slot = self->invList; | ||
921 | while (slot) { | ||
922 | slot = slot->next; | ||
923 | i++; | ||
924 | } | ||
925 | Com_DPrintf(DEBUG_SHARED0x02, "Used inventory slots %i (%s)\n", i, self->name); | ||
926 | return i; | ||
927 | } | ||
928 | |||
929 | /** | ||
930 | * @brief Initializes the inventory definition by linking the ->next pointers properly. | ||
931 | * @param[in] name The name that is shown in the output | ||
932 | * @param[out] interface The inventory interface pointer which should be initialized in this function. | ||
933 | * @param[in] csi The client-server-information structure | ||
934 | * @param[in] import Pointers to the lifecycle functions | ||
935 | * @sa G_Init | ||
936 | * @sa CL_InitLocal | ||
937 | */ | ||
938 | void INV_InitInventory (const char *name, inventoryInterface_t *interface, const csi_t* csi, const inventoryImport_t *import) | ||
939 | { | ||
940 | const item_t item = {NONE_AMMO0, NULL__null, NULL__null, 0, 0}; | ||
941 | |||
942 | OBJZERO(*interface)(memset(&((*interface)), (0), sizeof((*interface)))); | ||
943 | |||
944 | interface->import = import; | ||
945 | interface->name = name; | ||
946 | interface->cacheItem = item; | ||
947 | interface->csi = csi; | ||
948 | interface->invList = NULL__null; | ||
949 | |||
950 | interface->TryAddToInventory = I_TryAddToInventory; | ||
951 | interface->AddToInventory = I_AddToInventory; | ||
952 | interface->RemoveFromInventory = I_RemoveFromInventory; | ||
953 | interface->MoveInInventory = I_MoveInInventory; | ||
954 | interface->DestroyInventory = I_DestroyInventory; | ||
955 | interface->EmptyContainer = I_EmptyContainer; | ||
956 | interface->EquipActor = I_EquipActor; | ||
957 | interface->EquipActorMelee = I_EquipActorMelee; | ||
958 | interface->EquipActorRobot = I_EquipActorRobot; | ||
959 | interface->GetUsedSlots = I_GetUsedSlots; | ||
960 | } | ||
961 | |||
962 | void INV_DestroyInventory (inventoryInterface_t *interface) | ||
963 | { | ||
964 | if (interface->import == NULL__null) | ||
965 | return; | ||
966 | interface->import->FreeAll(); | ||
967 | OBJZERO(*interface)(memset(&((*interface)), (0), sizeof((*interface)))); | ||
968 | } |