1 /**
2  * @file
3  * @brief Common object-, inventory-, container- and firemode-related functions.
4  * @note Shared inventory management functions prefix: INVSH_
5  * @note Shared firemode management functions prefix: FIRESH_
6  */
7 
8 /*
9 Copyright (C) 2002-2013 UFO: Alien Invasion.
10 
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 See the GNU General Public License for more details.
21 
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25 
26 */
27 
28 #include "q_shared.h"
29 
30 static const csi_t* CSI;
31 
32 /**
33  * @brief Initializes client server shared data pointer. This works because the client and the server are both
34  * using exactly the same pointer.
35  * @param[in] import The client server interface pointer
36  * @sa G_Init
37  * @sa Com_ParseScripts
38  */
INVSH_InitCSI(const csi_t * import)39 void INVSH_InitCSI (const csi_t* import)
40 {
41 	CSI = import;
42 }
43 
44 /**
45  * @brief Checks whether the inventory definition is the floor
46  * @return @c true if the given inventory definition is of type floor
47  */
isFloorDef() const48 bool invDef_t::isFloorDef () const
49 {
50 	return id == CID_FLOOR;
51 }
52 
53 /**
54  * @brief Checks whether the inventory definition is the right Hand
55  * @return @c true if the given inventory definition is of type right
56  */
isRightDef() const57 bool invDef_t::isRightDef () const
58 {
59 	return id == CID_RIGHT;
60 }
61 
62 /**
63  * @brief Checks whether a given inventory definition is of special type
64  * @return @c true if the given inventory definition is of type left
65  */
isLeftDef() const66 bool invDef_t::isLeftDef () const
67 {
68 	return id == CID_LEFT;
69 }
70 
71 /**
72  * @brief Checks whether a given inventory definition is of special type
73  * @return @c true if the given inventory definition is of type equip
74  */
isEquipDef() const75 bool invDef_t::isEquipDef () const
76 {
77 	return id == CID_EQUIP;
78 }
79 
80 /**
81  * @brief Checks whether a given inventory definition is of special type
82  * @return @c true if the given inventory definition is of type armour
83  */
isArmourDef() const84 bool invDef_t::isArmourDef () const
85 {
86 	return id == CID_ARMOUR;
87 }
88 
89 static int cacheCheckToInventory = INV_DOES_NOT_FIT;
90 
91 /**
92  * @brief Will check if the item-shape is colliding with something else in the container-shape at position x/y.
93  * @note The function expects an already rotated shape for itemShape. Use getShapeRotated() if needed.
94  * @param[in] shape The shape of the container [SHAPE_BIG_MAX_HEIGHT]
95  * @param[in] itemShape The shape of the item [SHAPE_SMALL_MAX_HEIGHT]
96  * @param[in] x The x value in the container (1 << x in the shape bitmask)
97  * @param[in] y The y value in the container (SHAPE_BIG_MAX_HEIGHT is the max)
98  */
INVSH_CheckShapeCollision(const uint32_t * shape,const uint32_t itemShape,const int x,const int y)99 static bool INVSH_CheckShapeCollision (const uint32_t* shape, const uint32_t itemShape, const int x, const int y)
100 {
101 	int i;
102 
103 	/* Negative positions not allowed (all items are supposed to have at least one bit set in the first row and column) */
104 	if (x < 0 || y < 0) {
105 		Com_DPrintf(DEBUG_SHARED, "INVSH_CheckShapeCollision: x or y value negative: x=%i y=%i!\n", x, y);
106 		return true;
107 	}
108 
109 	for (i = 0; i < SHAPE_SMALL_MAX_HEIGHT; i++) {
110 		/* 0xFF is the length of one row in a "small shape" i.e. SHAPE_SMALL_MAX_WIDTH */
111 		const int itemRow = (itemShape >> (i * SHAPE_SMALL_MAX_WIDTH)) & 0xFF;
112 		/* Result has to be limited to 32bit (SHAPE_BIG_MAX_WIDTH) */
113 		const uint32_t itemRowShifted = itemRow << x;
114 
115 		/* Check x maximum. */
116 		if (itemRowShifted >> x != itemRow)
117 			/* Out of bounds (32bit; a few bits of this row in itemShape were truncated) */
118 			return true;
119 
120 		/* Check y maximum. */
121 		if (y + i >= SHAPE_BIG_MAX_HEIGHT && itemRow)
122 			/* This row (row "i" in itemShape) is outside of the max. bound and has bits in it. */
123 			return true;
124 
125 		/* Check for collisions of the item with the big mask. */
126 		if (itemRowShifted & shape[y + i])
127 			return true;
128 	}
129 
130 	return false;
131 }
132 
133 /**
134  * @brief Checks if an item-shape can be put into a container at a certain position... ignores any 'special' types of containers.
135  * @param[in] inv The inventory
136  * @param[in] container The container (index) to look into.
137  * @param[in] itemShape The shape info of an item to fit into the container.
138  * @param[in] x The x value in the container (1 << x in the shape bitmask)
139  * @param[in] y The y value in the container (SHAPE_BIG_MAX_HEIGHT is the max)
140  * @param[in] ignoredItem You can ignore one item in the container (most often the currently dragged one). Use nullptr if you want to check against all items in the container.
141  * @sa canHoldItem
142  * @return false if the item does not fit, true if it fits.
143  */
INVSH_CheckToInventory_shape(const Inventory * const inv,const invDef_t * container,const uint32_t itemShape,const int x,const int y,const Item * ignoredItem)144 static bool INVSH_CheckToInventory_shape (const Inventory* const inv, const invDef_t* container, const uint32_t itemShape, const int x, const int y, const Item* ignoredItem)
145 {
146 	static uint32_t mask[SHAPE_BIG_MAX_HEIGHT];
147 
148 	assert(container);
149 
150 	if (container->scroll)
151 		Sys_Error("INVSH_CheckToInventory_shape: No scrollable container will ever use this. This type does not support grid-packing!");
152 
153 	/* check bounds */
154 	if (x < 0 || y < 0 || x >= SHAPE_BIG_MAX_WIDTH || y >= SHAPE_BIG_MAX_HEIGHT)
155 		return false;
156 
157 	if (!cacheCheckToInventory) {
158 		int j;
159 		/* extract shape info */
160 		for (j = 0; j < SHAPE_BIG_MAX_HEIGHT; j++)
161 			mask[j] = ~container->shape[j];
162 
163 		/* Add other items to mask. (i.e. merge their shapes at their location into the generated mask) */
164 		const Container &cont = inv->getContainer(container->id);
165 		Item* item = nullptr;
166 		while ((item = cont.getNextItem(item))) {
167 			if (ignoredItem == item)
168 				continue;
169 
170 			if (item->rotated)
171 				INVSH_MergeShapes(mask, item->def()->getShapeRotated(), item->getX(), item->getY());
172 			else
173 				INVSH_MergeShapes(mask, item->def()->shape, item->getX(), item->getY());
174 		}
175 	}
176 
177 	/* Test for collisions with newly generated mask. */
178 	if (INVSH_CheckShapeCollision(mask, itemShape, x, y))
179 		return false;
180 
181 	/* Everything ok. */
182 	return true;
183 }
184 
185 /**
186  * @brief Checks the shape if there is a 1-bit on the position x/y.
187  * @param[in] shape The shape to check in. (8x4)
188  * @param[in] x The x value in the shape (1 << x in the shape bitmask)
189  * @param[in] y The y value in the shape (SHAPE_SMALL_MAX_HEIGHT is the max)
190  */
INVSH_CheckShapeSmall(const uint32_t shape,const int x,const int y)191 static bool INVSH_CheckShapeSmall (const uint32_t shape, const int x, const int y)
192 {
193 	if (y >= SHAPE_SMALL_MAX_HEIGHT || x >= SHAPE_SMALL_MAX_WIDTH|| x < 0 || y < 0) {
194 		return false;
195 	}
196 
197 	return shape & (0x01 << (y * SHAPE_SMALL_MAX_WIDTH + x));
198 }
199 
200 /**
201 
202  * @brief Check if a position in a container is used by an item (i.e. collides with the shape).
203  * @param[in] item A pointer to the Item in question.
204  * @param[in] x The x location in the container.
205  * @param[in] y The y location in the container.
206  */
INVSH_ShapeCheckPosition(const Item * item,const int x,const int y)207 static bool INVSH_ShapeCheckPosition (const Item* item, const int x, const int y)
208 {
209 	uint32_t shape;
210 
211 	assert(item);
212 
213 	/* Check if the position is inside the shape (depending on rotation value) of the item. */
214 	if (item->rotated) {
215 		shape = item->def()->getShapeRotated();
216 	} else {
217 		shape = item->def()->shape;
218 	}
219 
220 	return INVSH_CheckShapeSmall(shape, x - item->getX(), y - item->getY());
221 }
222 
223 /**
224  * @brief Checks whether a given item is an aircraftitem item
225  * @note This is done by checking whether it's a craftitem and not
226  * marked as a dummy item - the combination of both means, that it's a
227  * basedefence item.
228  * @return true if the given item is an aircraftitem item
229  * @sa isBaseDefenceItem
230  */
isCraftItem() const231 bool objDef_t::isCraftItem () const
232 {
233 	return craftitem.type != MAX_ACITEMS && !isDummy;
234 }
235 
236 /**
237  * @brief Checks whether the item is a basedefence item
238  * @note This is done by checking whether it's a craftitem and whether it's
239  * marked as a dummy item - the combination of both means, that it's a
240  * basedefence item.
241  * @return true if the given item is a basedefence item
242  * @sa isCraftItem
243  */
isBaseDefenceItem() const244 bool objDef_t::isBaseDefenceItem () const
245 {
246 	return craftitem.type != MAX_ACITEMS && isDummy;
247 }
248 
249 /**
250  * @brief Returns the item that belongs to the given id or @c nullptr if it wasn't found.
251  * @param[in] id the item id in our object definition array (csi.ods)
252  * @sa INVSH_GetItemByID
253  */
INVSH_GetItemByIDSilent(const char * id)254 const objDef_t* INVSH_GetItemByIDSilent (const char* id)
255 {
256 	if (!id)
257 		return nullptr;
258 
259 	for (int i = 0; i < CSI->numODs; ++i) {	/* i = item index */
260 		const objDef_t* item = &CSI->ods[i];
261 		if (Q_streq(id, item->id)) {
262 			return item;
263 		}
264 	}
265 	return nullptr;
266 }
267 
268 /**
269  * @brief Returns the item that belongs to the given index or @c nullptr if the index is invalid.
270  */
INVSH_GetItemByIDX(int index)271 const objDef_t* INVSH_GetItemByIDX (int index)
272 {
273 	if (index == NONE)
274 		return nullptr;
275 
276 	if (index < 0 || index >= CSI->numODs)
277 		Sys_Error("Invalid object index given: %i", index);
278 
279 	return &CSI->ods[index];
280 }
281 
282 /**
283  * @brief Returns the item that belongs to the given id or @c nullptr if it wasn't found.
284  * @param[in] id the item id in our object definition array (csi.ods)
285  * @sa INVSH_GetItemByIDSilent
286  */
INVSH_GetItemByID(const char * id)287 const objDef_t* INVSH_GetItemByID (const char* id)
288 {
289 	const objDef_t* od = INVSH_GetItemByIDSilent(id);
290 	if (!od)
291 		Com_Printf("INVSH_GetItemByID: Item \"%s\" not found.\n", id);
292 
293 	return od;
294 }
295 
INVSH_GetImplantForObjDef(const objDef_t * od)296 const implantDef_t* INVSH_GetImplantForObjDef (const objDef_t* od)
297 {
298 	for (int i = 0; i < CSI->numImplants; i++) {
299 		const implantDef_t* id = &CSI->implants[i];
300 		if (id->item == od)
301 			return id;
302 	}
303 	Com_Printf("INVSH_GetImplantForObjDef: could not get implant for %s\n", od->id);
304 	return nullptr;
305 }
306 
307 /**
308  * @brief Returns the implant that belongs to the given id or @c nullptr if it wasn't found.
309  * @param[in] id the implant id in our implant definition array (csi.implants)
310  * @sa INVSH_GetImplantByID
311  */
INVSH_GetImplantByIDSilent(const char * id)312 const implantDef_t* INVSH_GetImplantByIDSilent (const char* id)
313 {
314 	if (!id)
315 		return nullptr;
316 
317 	for (int i = 0; i < CSI->numImplants; ++i) {
318 		const implantDef_t* item = &CSI->implants[i];
319 		if (Q_streq(id, item->id)) {
320 			return item;
321 		}
322 	}
323 	return nullptr;
324 }
325 
326 /**
327  * @brief Returns the implant that belongs to the given id or @c nullptr if it wasn't found.
328  * @param[in] id the implant id in our implant definition array (csi.implants)
329  * @sa INVSH_GetImplantByIDSilent
330  */
INVSH_GetImplantByID(const char * id)331 const implantDef_t* INVSH_GetImplantByID (const char* id)
332 {
333 	const implantDef_t* implantDef = INVSH_GetImplantByIDSilent(id);
334 	if (!implantDef)
335 		Com_Printf("INVSH_GetImplantByID: Implant \"%s\" not found.\n", id);
336 
337 	return implantDef;
338 }
339 
340 /**
341  * Searched an inventory container by a given container id
342  * @param[in] id ID or name of the inventory container to search for
343  * @return @c nullptr if not found
344  */
INVSH_GetInventoryDefinitionByID(const char * id)345 const invDef_t* INVSH_GetInventoryDefinitionByID (const char* id)
346 {
347 	containerIndex_t i;
348 	const invDef_t* container;
349 
350 	for (i = 0, container = CSI->ids; i < CID_MAX; ++container, ++i)
351 		if (Q_streq(id, container->name))
352 			return container;
353 
354 	return nullptr;
355 }
356 
357 /**
358  * @brief Checks if an item can be used to reload a weapon.
359  * @param[in] weapon The weapon (in the inventory) to check the item with. Might not be @c nullptr.
360  * @return @c true if the item can be used in the given weapon, otherwise @c false.
361  */
isLoadableInWeapon(const objDef_t * weapon) const362 bool objDef_t::isLoadableInWeapon (const objDef_t* weapon) const
363 {
364 	int i;
365 
366 	assert(weapon);
367 
368 	/* check whether the weapon is only linked to itself. */
369 	if (this->numWeapons == 1 && this->weapons[0] == this)
370 		return false;
371 
372 	for (i = 0; i < this->numWeapons; i++)
373 		if (weapon == this->weapons[i])
374 			return true;
375 
376 	return false;
377 }
378 
379 /*
380 ===============================
381 FIREMODE MANAGEMENT FUNCTIONS
382 ===============================
383 */
384 
385 /**
386  * @brief Get the fire definitions for a given object
387  * @param[in] obj The object to get the firedef for
388  * @param[in] weapFdsIdx the weapon index in the fire definition array
389  * @param[in] fdIdx the fire definition index for the weapon (given by @c weapFdsIdx)
390  * @return Will never return nullptr
391  * @sa getFiredefs
392  */
FIRESH_GetFiredef(const objDef_t * obj,const weaponFireDefIndex_t weapFdsIdx,const fireDefIndex_t fdIdx)393 const fireDef_t* FIRESH_GetFiredef (const objDef_t* obj, const weaponFireDefIndex_t weapFdsIdx, const fireDefIndex_t fdIdx)
394 {
395 	if (weapFdsIdx < 0 || weapFdsIdx >= MAX_WEAPONS_PER_OBJDEF)
396 		Sys_Error("FIRESH_GetFiredef: weapFdsIdx out of bounds [%i] for item '%s'", weapFdsIdx, obj->id);
397 	if (fdIdx < 0 || fdIdx >= MAX_FIREDEFS_PER_WEAPON)
398 		Sys_Error("FIRESH_GetFiredef: fdIdx out of bounds [%i] for item '%s'", fdIdx, obj->id);
399 	return &obj->fd[weapFdsIdx & (MAX_WEAPONS_PER_OBJDEF - 1)][fdIdx & (MAX_FIREDEFS_PER_WEAPON - 1)];
400 }
401 
402 /**
403  * @brief Will merge the second shape (=itemShape) into the first one (=big container shape) on the position x/y.
404  * @note The function expects an already rotated shape for itemShape. Use getShapeRotated() if needed.
405  * @param[in] shape The shape of the container [SHAPE_BIG_MAX_HEIGHT]'
406  * @param[in] itemShape The shape of the item [SHAPE_SMALL_MAX_HEIGHT]
407  * @param[in] x The x value in the container (1 << x in the shape bitmask)
408  * @param[in] y The y value in the container (SHAPE_BIG_MAX_HEIGHT is the max)
409  */
INVSH_MergeShapes(uint32_t * shape,const uint32_t itemShape,const int x,const int y)410 void INVSH_MergeShapes (uint32_t* shape, const uint32_t itemShape, const int x, const int y)
411 {
412 	for (int i = 0; (i < SHAPE_SMALL_MAX_HEIGHT) && (y + i < SHAPE_BIG_MAX_HEIGHT); ++i)
413 		shape[y + i] |= ((itemShape >> i * SHAPE_SMALL_MAX_WIDTH) & 0xFF) << x;
414 }
415 
416 /**
417  * @brief Checks the shape if there is a 1-bit on the position x/y.
418  * @param[in] shape Pointer to 'uint32_t shape[SHAPE_BIG_MAX_HEIGHT]'
419  * @param[in] x The x value in the container (1 << x in the shape bitmask)
420  * @param[in] y The y value in the container (SHAPE_BIG_MAX_HEIGHT is the max)
421  */
INVSH_CheckShape(const uint32_t * shape,const int x,const int y)422 bool INVSH_CheckShape (const uint32_t* shape, const int x, const int y)
423 {
424 	const uint32_t row = shape[y];
425 	const int position = powf(2.0f, (float)x);
426 
427 	if (y >= SHAPE_BIG_MAX_HEIGHT || x >= SHAPE_BIG_MAX_WIDTH || x < 0 || y < 0) {
428 		Com_Printf("INVSH_CheckShape: Bad x or y value: (x=%i, y=%i)\n", x, y);
429 		return false;
430 	}
431 
432 	if ((row & position) == 0)
433 		return false;
434 
435 	return true;
436 }
437 
438 /**
439  * @brief Counts the used bits in a shape (item shape).
440  * @param[in] shape The shape to count the bits in.
441  * @return Number of bits.
442  * @note Used to calculate the real field usage in the inventory
443  */
INVSH_ShapeSize(const uint32_t shape)444 int INVSH_ShapeSize (const uint32_t shape)
445 {
446 	int bitCounter = 0;
447 
448 	for (int i = 0; i < SHAPE_SMALL_MAX_HEIGHT * SHAPE_SMALL_MAX_WIDTH; ++i)
449 		if (shape & (1 << i))
450 			++bitCounter;
451 
452 	return bitCounter;
453 }
454 
455 /**
456  * @brief Sets one bit in a shape to true/1
457  * @note Only works for V_SHAPE_SMALL!
458  * If the bit is already set the shape is not changed.
459  * @param[in] shape The shape to modify. (8x4)
460  * @param[in] x The x (width) position of the bit to set.
461  * @param[in] y The y (height) position of the bit to set.
462  * @return The new shape.
463  */
INVSH_ShapeSetBit(uint32_t shape,const int x,const int y)464 static uint32_t INVSH_ShapeSetBit (uint32_t shape, const int x, const int y)
465 {
466 	if (x >= SHAPE_SMALL_MAX_WIDTH || y >= SHAPE_SMALL_MAX_HEIGHT || x < 0 || y < 0) {
467 		Com_Printf("INVSH_ShapeSetBit: Bad x or y value: (x=%i, y=%i)\n", x,y);
468 		return shape;
469 	}
470 
471 	shape |= 0x01 << (y * SHAPE_SMALL_MAX_WIDTH + x);
472 	return shape;
473 }
474 
475 
476 /**
477  * @brief Rotates a shape definition 90 degree to the left (CCW)
478  * @note Only works for V_SHAPE_SMALL!
479  * @return The new shape.
480  */
getShapeRotated() const481 uint32_t objDef_t::getShapeRotated () const
482 {
483 	int h, w;
484 	uint32_t shapeNew = 0;
485 	int maxWidth = -1;
486 
487 	for (w = SHAPE_SMALL_MAX_WIDTH - 1; w >= 0; w--) {
488 		for (h = 0; h < SHAPE_SMALL_MAX_HEIGHT; h++) {
489 			if (!INVSH_CheckShapeSmall(shape, w, h))
490 				continue;
491 			if (w >= SHAPE_SMALL_MAX_HEIGHT) {
492 				/* Object can't be rotated (code-wise), it is longer than SHAPE_SMALL_MAX_HEIGHT allows. */
493 				return shape;
494 			}
495 
496 			if (maxWidth < 0)
497 				maxWidth = w;
498 
499 			shapeNew = INVSH_ShapeSetBit(shapeNew, h, maxWidth - w);
500 		}
501 	}
502 
503 	return shapeNew;
504 }
505 
506 /** @brief Item constructor with all default values */
Item()507 Item::Item ()
508 {
509 	setAmmoLeft(NONE_AMMO);
510 	setAmount(0);
511 	setAmmoDef(nullptr);
512 	_itemDef = nullptr;
513 	_x = _y = rotated = 0;
514 	_next = nullptr;
515 }
516 
517 /** @brief Item constructor with the 3 most often changed attributes */
Item(const objDef_t * itemDef,const objDef_t * ammo,int ammoLeft)518 Item::Item (const objDef_t* itemDef, const objDef_t* ammo, int ammoLeft)
519 {
520 	setAmmoLeft(ammoLeft);
521 	setAmount(0);
522 	setAmmoDef(ammo);
523 	_itemDef = itemDef;
524 	_x = _y = rotated = 0;
525 	_next = nullptr;
526 }
527 
528 /**
529  * @brief Return the weight of an item.
530  * @return The weight of the given item including any ammo loaded.
531  */
getWeight() const532 float Item::getWeight () const
533 {
534 	float weight = def()->weight;
535 	if (ammoDef() && ammoDef() != def() && getAmmoLeft() > 0) {
536 		weight += ammoDef()->weight;
537 	}
538 	return weight;
539 }
540 
541 /**
542  * @brief Check if the (physical) information of 2 items is exactly the same.
543  * @param[in] other Second item to compare.
544  * @return true if they are identical or false otherwise.
545  */
isSameAs(const Item * const other) const546 bool Item::isSameAs (const Item* const other) const
547 {
548 	if (this == other)
549 		return true;
550 
551 	if (this == nullptr || other == nullptr)
552 		return false;
553 
554 	if (this->def() == other->def() && this->ammoDef() == other->ammoDef() && this->getAmmoLeft() == other->getAmmoLeft())
555 		return true;
556 
557 	return false;
558 }
559 
560 /**
561  * @brief Calculates the first "true" bit in the shape and returns its position in the item.
562  * @note Use this to get the first "grab-able" grid-location of an item (not in the container !).
563  * @param[out] x The x location inside the item.
564  * @param[out] y The x location inside the item.
565  * @sa canHoldItem
566  */
getFirstShapePosition(int * const x,int * const y) const567 void Item::getFirstShapePosition (int* const x, int* const y) const
568 {
569 	int tempX, tempY;
570 
571 	for (tempX = 0; tempX < SHAPE_SMALL_MAX_HEIGHT; tempX++)
572 		for (tempY = 0; tempY < SHAPE_SMALL_MAX_HEIGHT; tempY++)
573 			if (INVSH_ShapeCheckPosition(this, this->getX() + tempX, this->getY() + tempY)) {
574 				*x = tempX;
575 				*y = tempY;
576 				return;
577 			}
578 
579 	*x = *y = NONE;
580 }
581 
582 /**
583  * @brief Returns the firedefinitions for a given weapon/ammo
584  * @return The array (one-dimensional) of the firedefs of the ammo for a given weapon, or @c nullptr if the ammo
585  * doesn't support the given weapon
586  * @sa FIRESH_GetFiredef
587  */
getFiredefs() const588 const fireDef_t* Item::getFiredefs () const
589 {
590 	const objDef_t* ammodef = ammoDef();
591 	const objDef_t* weapon = def();
592 
593 	/* this weapon does not use ammo, check for
594 	 * existing firedefs in the weapon. */
595 	if (weapon->numWeapons > 0)
596 		ammodef = def();
597 
598 	if (!ammodef)
599 		return nullptr;
600 
601 	for (int i = 0; i < ammodef->numWeapons; ++i) {
602 		if (weapon == ammodef->weapons[i])
603 			return &ammodef->fd[i][0];
604 	}
605 
606 	return nullptr;
607 }
608 
609 /**
610  * @brief Get the firedef that uses the most TU for this item.
611  * @return The firedef that uses the most TU for this item or @c nullptr.
612  */
getSlowestFireDef() const613 const fireDef_t* Item::getSlowestFireDef () const
614 {
615 	const fireDef_t* fdArray = getFiredefs();
616 	int slowest = 0;
617 
618 	if (fdArray == nullptr)
619 		return nullptr;
620 
621 	for (int i = 0; i < MAX_FIREDEFS_PER_WEAPON; ++i)
622 		if (fdArray[i].time > fdArray[slowest].time)
623 			slowest = i;
624 
625 	return &fdArray[slowest];
626 }
627 
628 /**
629  * @brief Checks whether this item is a reaction fire enabled weapon.
630  * @note The item is supposed to be in the right or left hand
631  * @return @c nullptr if no reaction fire enabled weapon, the
632  * reaction fire enabled object otherwise.
633  */
getReactionFireWeaponType() const634 const objDef_t* Item::getReactionFireWeaponType () const
635 {
636 	if (!this)
637 		return nullptr;
638 
639 	if (def()) {
640 		const fireDef_t* fd = getFiredefs();
641 		if (fd && fd->reaction)
642 			return def();
643 	}
644 
645 	return nullptr;
646 }
647 
Container()648 Container::Container ()
649 {
650 	_def = nullptr;
651 	_invList = nullptr;
652 	id = 0;
653 }
654 
def() const655 const invDef_t* Container::def () const
656 {
657 	return &CSI->ids[id];
658 }
getNextItem(const Item * prev) const659 Item* Container::getNextItem (const Item* prev) const
660 {
661 	if (!prev)
662 		return _invList;	/* the first one */
663 
664 	return prev->getNext();
665 }
666 
667 /** @brief Count the number of items in the Container
668  * @note For temp containers we count the number of different items */
countItems() const669 int Container::countItems () const
670 {
671 	int nr = 0;
672 	Item* item = nullptr;
673 	while ((item = getNextItem(item))) {
674 		/** For temp containers, we neglect Item::amount. */
675 		++nr;
676 	}
677 	return nr;
678 }
679 
Inventory()680 Inventory::Inventory ()
681 {
682 	/* This (prototype-)constructor does not work as intended. Seems like the first inventory is created before CSI is set.
683 	 * There is the static game_inventory, static character_t, static le_t ...
684 	 * containers[CID_LEFT]._invList = nullptr;
685 	 * containers[CID_LEFT]._def = &CSI->ids[CID_LEFT]; */
686 
687 	/* Plan B: add an 'id' member to class Container and init it here */
688 	init();
689 }
690 
init()691 void Inventory::init ()
692 {
693 	OBJZERO(_containers);
694 	for (int i = 0; i < CID_MAX; ++i)
695 		_containers[i].id = i;
696 }
697 
_getNextCont(const Container * prev) const698 const Container* Inventory::_getNextCont (const Container* prev) const
699 {
700 	if (!prev)
701 		/* the first one */
702 		return &_containers[0];
703 	if (prev >= &_containers[CID_MAX - 1])
704 		/* prev was the last one */
705 		return nullptr;
706 
707 	return ++prev;
708 }
709 
getNextCont(const Container * prev,bool inclTemp) const710 const Container* Inventory::getNextCont (const Container* prev, bool inclTemp) const
711 {
712 	const Container* cont = prev;
713 
714 	while ((cont = _getNextCont(cont))) {
715 		/* if we don't want to include the temp containers ... */
716 		if (!inclTemp) {
717 			/* ...skip them ! */
718 			if (cont == &_containers[CID_FLOOR])
719 				continue;
720 			if (cont == &_containers[CID_EQUIP])
721 				continue;
722 		}
723 		break;
724 	}
725 	return cont;
726 }
727 
728 /** @brief Count the number of items in the inventory (without temp containers) */
countItems() const729 int Inventory::countItems () const
730 {
731 	int nr = 0;
732 	const Container* cont = nullptr;
733 	while ((cont = getNextCont(cont))) {	/* skips the temp containers */
734 		nr += cont->countItems();
735 	}
736 	return nr;
737 }
738 /**
739  * @param[in] container The index of the container in the inventory to check the item in.
740  * @param[in] od The type of item to check in the inventory.
741  * @param[in] x The x value in the container (1 << x in the shape bitmask)
742  * @param[in] y The y value in the container (SHAPE_BIG_MAX_HEIGHT is the max)
743  * @param[in] ignoredItem You can ignore one item in the container (most often the currently dragged one). Use nullptr if you want to check against all items in the container.
744  * @return INV_DOES_NOT_FIT if the item does not fit
745  * @return INV_FITS if it fits and
746  * @return INV_FITS_ONLY_ROTATED if it fits only when rotated 90 degree (to the left).
747  * @return INV_FITS_BOTH if it fits either normally or when rotated 90 degree (to the left).
748  */
canHoldItem(const invDef_t * container,const objDef_t * od,const int x,const int y,const Item * ignoredItem) const749 int Inventory::canHoldItem (const invDef_t* container, const objDef_t* od, const int x, const int y, const Item* ignoredItem) const
750 {
751 	int fits;
752 	assert(container);
753 	assert(od);
754 
755 	/* armour vs item */
756 	if (od->isArmour()) {
757 		if (!container->armour && !container->all) {
758 			return INV_DOES_NOT_FIT;
759 		}
760 	} else if (!od->implant && container->implant) {
761 		return INV_DOES_NOT_FIT;
762 	} else if (!od->headgear && container->headgear) {
763 		return INV_DOES_NOT_FIT;
764 	} else if (container->armour) {
765 		return INV_DOES_NOT_FIT;
766 	}
767 
768 	/* twohanded item */
769 	if (od->holdTwoHanded) {
770 		if ((container->isRightDef() && getContainer2(CID_LEFT)) || container->isLeftDef())
771 			return INV_DOES_NOT_FIT;
772 	}
773 
774 	/* left hand is busy if right wields twohanded */
775 	if (container->isLeftDef()) {
776 		if (getContainer2(CID_RIGHT) && getContainer2(CID_RIGHT)->isHeldTwoHanded())
777 			return INV_DOES_NOT_FIT;
778 
779 		/* can't put an item that is 'fireTwoHanded' into the left hand */
780 		if (od->fireTwoHanded)
781 			return INV_DOES_NOT_FIT;
782 	}
783 
784 	if (container->unique) {
785 		const Item item(od);
786 		if (containsItem(container->id, &item))
787 			return INV_DOES_NOT_FIT;
788 	}
789 
790 	/* Single item containers, e.g. hands or headgear. */
791 	if (container->single) {
792 		if (getContainer2(container->id)) {
793 			/* There is already an item. */
794 			return INV_DOES_NOT_FIT;
795 		} else {
796 			fits = INV_DOES_NOT_FIT; /* equals 0 */
797 
798 			if (INVSH_CheckToInventory_shape(this, container, od->shape, x, y, ignoredItem))
799 				fits |= INV_FITS;
800 			if (INVSH_CheckToInventory_shape(this, container, od->getShapeRotated(), x, y, ignoredItem))
801 				fits |= INV_FITS_ONLY_ROTATED;
802 
803 			if (fits != INV_DOES_NOT_FIT)
804 				return fits;	/**< Return INV_FITS_BOTH if both if statements where true above. */
805 
806 			Com_DPrintf(DEBUG_SHARED, "canHoldItem: INFO: Moving to 'single' container but item would not fit normally.\n");
807 			return INV_FITS; /**< We are returning with status true (1) if the item does not fit at all - unlikely but not impossible. */
808 		}
809 	}
810 
811 	/* Scrolling container have endless room, the item always fits. */
812 	if (container->scroll)
813 		return INV_FITS;
814 
815 	/* Check 'grid' containers. */
816 	fits = INV_DOES_NOT_FIT; /* equals 0 */
817 	if (INVSH_CheckToInventory_shape(this, container, od->shape, x, y, ignoredItem))
818 		fits |= INV_FITS;
819 	/** @todo aren't both (equip and floor) temp container? */
820 	if (!container->isEquipDef() && !container->isFloorDef()
821 	&& INVSH_CheckToInventory_shape(this, container, od->getShapeRotated(), x, y, ignoredItem))
822 		fits |= INV_FITS_ONLY_ROTATED;
823 
824 	return fits;	/**< Return INV_FITS_BOTH if both if statements where true above. */
825 }
826 
827 /**
828  * @brief Searches if there is an item at location (x,y) in a container.
829  * @param[in] container Container in the inventory.
830  * @param[in] x/y Position in the container that you want to check.
831  * @return Item Pointer to the Item/item that is located at x/y.
832  */
getItemAtPos(const invDef_t * container,const int x,const int y) const833 Item* Inventory::getItemAtPos (const invDef_t* container, const int x, const int y) const
834 {
835 	assert(container);
836 
837 	/* Only a single item. */
838 	if (container->single)
839 		return getContainer2(container->id);
840 
841 	if (container->scroll)
842 		Sys_Error("getItemAtPos: Scrollable containers (%i:%s) are not supported by this function.", container->id, container->name);
843 
844 	/* More than one item - search for the item that is located at location x/y in this container. */
845 	const Container &cont = getContainer(container->id);
846 	Item* item = nullptr;
847 	while ((item = cont.getNextItem(item)))
848 		if (INVSH_ShapeCheckPosition(item, x, y))
849 			return item;
850 
851 	/* Found nothing. */
852 	return nullptr;
853 }
854 
855 /**
856  * @brief Finds space for item in inv at container
857  * @param[in] item The item to check the space for
858  * @param[in] container The container to search in
859  * @param[out] px The x position in the container
860  * @param[out] py The y position in the container
861  * @param[in] ignoredItem You can ignore one item in the container (most often the currently dragged one). Use nullptr if you want to check against all items in the container.
862  * @sa canHoldItem
863  * @note x and y are NONE if no free space is available
864  */
findSpace(const invDef_t * container,const Item * item,int * const px,int * const py,const Item * ignoredItem) const865 void Inventory::findSpace (const invDef_t* container, const Item* item, int* const px, int* const py, const Item* ignoredItem) const
866 {
867 	assert(container);
868 	assert(!cacheCheckToInventory);
869 
870 	/* Scrollable container always have room. We return a dummy location. */
871 	if (container->scroll) {
872 		*px = *py = 0;
873 		return;
874 	}
875 
876 	/** @todo optimize for single containers */
877 
878 	for (int y = 0; y < SHAPE_BIG_MAX_HEIGHT; ++y) {
879 		for (int x = 0; x < SHAPE_BIG_MAX_WIDTH; ++x) {
880 			const int checkedTo = canHoldItem(container, item->def(), x, y, ignoredItem);
881 			if (checkedTo) {
882 				cacheCheckToInventory = INV_DOES_NOT_FIT;
883 				*px = x;
884 				*py = y;
885 				return;
886 			} else {
887 				cacheCheckToInventory = INV_FITS;
888 			}
889 		}
890 	}
891 	cacheCheckToInventory = INV_DOES_NOT_FIT;
892 
893 #ifdef PARANOID
894 	Com_DPrintf(DEBUG_SHARED, "findSpace: no space for %s: %s in %s\n",
895 		item->def()->type, item->def()->id, container->name);
896 #endif
897 	*px = *py = NONE;
898 }
899 
900 /**
901  * @brief Check that adding an item to the inventory won't exceed the max permitted weight.
902  * @param[in] from Index of the container the item comes from.
903  * @param[in] to Index of the container the item is being placed.
904  * @param[in] item The item that is being added.
905  * @param[in] maxWeight The max permitted weight.
906  * @return @c true if it is Ok to add the item @c false otherwise.
907  */
canHoldItemWeight(containerIndex_t from,containerIndex_t to,const Item & item,int maxWeight) const908 bool Inventory::canHoldItemWeight (containerIndex_t from, containerIndex_t to, const Item &item, int maxWeight) const
909 {
910 	if (CSI->ids[to].temp || !CSI->ids[from].temp)
911 		return true;
912 
913 	const float itemWeight = item.getWeight();
914 	if (itemWeight <= 0.00001f)
915 		return true;
916 	const bool swapArmour = item.isArmour() && getArmour();
917 	const float invWeight = getWeight() - (swapArmour ? getArmour()->getWeight() : 0);
918 
919 	return (maxWeight < 0 || maxWeight >= invWeight + itemWeight);
920 }
921 
922 /**
923  * @brief Get the weight of the items in the given inventory (excluding those in temp containers).
924  * @return The total weight of the inventory items (excluding those in temp containers)
925  */
getWeight() const926 float Inventory::getWeight () const
927 {
928 	float weight = 0;
929 	const Container* cont = nullptr;
930 	while ((cont = getNextCont(cont))) {
931 		Item* item = nullptr;
932 		while ((item = cont->getNextItem(item))) {
933 			weight += item->getWeight();
934 		}
935 	}
936 	return weight;
937 }
938 
setFloorContainer(Item * cont)939 void Inventory::setFloorContainer(Item* cont)
940 {
941 	setContainer(CID_FLOOR, cont);
942 }
943 
getRightHandContainer() const944 Item* Inventory::getRightHandContainer () const
945 {
946 	return getContainer2(CID_RIGHT);
947 }
948 
getLeftHandContainer() const949 Item* Inventory::getLeftHandContainer () const
950 {
951 	return getContainer2(CID_LEFT);
952 }
953 
getHeadgear() const954 Item* Inventory::getHeadgear () const
955 {
956 	return getContainer2(CID_HEADGEAR);
957 }
958 
getHolsterContainer() const959 Item* Inventory::getHolsterContainer () const
960 {
961 	return getContainer2(CID_HOLSTER);
962 }
963 
getFloorContainer() const964 Item* Inventory::getFloorContainer () const
965 {
966 	return getContainer2(CID_FLOOR);
967 }
968 
getEquipContainer() const969 Item* Inventory::getEquipContainer () const
970 {
971 	return getContainer2(CID_EQUIP);
972 }
973 
getArmour() const974 Item* Inventory::getArmour () const
975 {
976 	return getContainer2(CID_ARMOUR);
977 }
978 
979 /**
980  * @brief Searches a specific item in the inventory&container.
981  * @param[in] contId Container in the inventory.
982  * @param[in] searchItem The item to search for.
983  * @return Pointer to the first item of this type found, otherwise @c nullptr.
984  */
findInContainer(const containerIndex_t contId,const Item * const searchItem) const985 Item* Inventory::findInContainer (const containerIndex_t contId, const Item* const searchItem) const
986 {
987 	const Container &cont = getContainer(contId);
988 	Item* item = nullptr;
989 	while ((item = cont.getNextItem(item)))
990 		if (item->isSameAs(searchItem)) {
991 			return item;
992 		}
993 
994 	return nullptr;
995 }
996 
997 /**
998  * @brief Checks if there is a weapon in the hands that can be used for reaction fire.
999  */
holdsReactionFireWeapon() const1000 bool Inventory::holdsReactionFireWeapon () const
1001 {
1002 	if (getRightHandContainer()->getReactionFireWeaponType())
1003 		return true;
1004 	if (getLeftHandContainer()->getReactionFireWeaponType())
1005 		return true;
1006 	return false;
1007 }
1008 
1009 /**
1010  * @brief Combine the rounds of partially used clips.
1011  */
addClip(const Item * item)1012 void equipDef_t::addClip (const Item* item)
1013 {
1014 	const int ammoIdx = item->ammoDef()->idx;
1015 	numItemsLoose[ammoIdx] += item->getAmmoLeft();
1016 	/* Accumulate loose ammo into clips */
1017 	if (numItemsLoose[ammoIdx] >= item->def()->ammo) {
1018 		numItemsLoose[ammoIdx] -= item->def()->ammo;
1019 		numItems[ammoIdx]++;
1020 	}
1021 }
1022