UFO: Alien Invasion
Loading...
Searching...
No Matches
g_inventory.cpp
Go to the documentation of this file.
1
4
5/*
6Copyright (C) 2002-2025 UFO: Alien Invasion.
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17See the GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; if not, write to the Free Software
21Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23*/
24
25#include "g_inventory.h"
26#include "g_client.h"
27#include "g_spawn.h"
28#include "g_utils.h"
29#include "g_vis.h"
30
31const equipDef_t* G_GetEquipDefByID (const char* equipID)
32{
33 const equipDef_t* ed = gi.csi->eds;
34
35 for (int i = 0; i < gi.csi->numEDs; i++, ed++)
36 if (Q_streq(equipID, ed->id))
37 return ed;
38
39 gi.DPrintf("Could not find the equipment with the id: '%s'\n", equipID);
40 return nullptr;
41}
42
49{
50 return G_GetEdictFromPos(pos, ET_ITEM);
51}
52
60{
61 Edict* floor = G_GetFloorItemFromPos(ent->pos);
62 /* found items */
63 if (floor) {
64 ent->setFloor(floor);
65 return floor;
66 }
67
68 /* no items on ground found */
69 ent->resetFloor();
70 return nullptr;
71}
72
80bool G_InventoryRemoveItemByID (const char* itemID, Edict* ent, containerIndex_t container)
81{
82 Item* ic = ent->getContainer(container);
83 while (ic) {
84 const objDef_t* item = ic->def();
85 if (item != nullptr && Q_streq(item->id, itemID)) {
86 /* remove the virtual item to update the inventory lists */
87 if (!game.invi.removeFromInventory(&ent->chr.inv, INVDEF(container), ic))
88 gi.Error("Could not remove item '%s' from inventory %i",
89 ic->def()->id, container);
90 G_EventInventoryDelete(*ent, G_VisToPM(ent->visflags), container, ic->getX(), ic->getY());
91 return true;
92 }
93 ic = ic->getNext();
94 }
95
96 return false;
97}
98
108{
109 if (container == CID_ARMOUR || container == CID_IMPLANT)
110 return false;
111
112 Item* ic = ent->getContainer(container);
113 if (!ic)
114 return false;
115
116 bool check = false;
117 while (ic) {
118 assert(ic->def());
119 if (ic->def()->isVirtual) {
120 Item* next = ic->getNext();
121 /* remove the virtual item to update the inventory lists */
122 if (!game.invi.removeFromInventory(&ent->chr.inv, INVDEF(container), ic))
123 gi.Error("Could not remove virtual item '%s' from inventory %i",
124 ic->def()->id, container);
125 ic = next;
126 } else {
127 /* there are none virtual items left that should be send to the client */
128 check = true;
129 ic = ic->getNext();
130 }
131 }
132 return check;
133}
134
140bool G_AddItemToFloor (const pos3_t pos, const char* itemID)
141{
142 const objDef_t* od = INVSH_GetItemByIDSilent(itemID);
143 if (!od) {
144 gi.DPrintf("Could not find item '%s'\n", itemID);
145 return false;
146 }
147
148 /* Also sets FLOOR(ent) to correct value. */
149 Edict* floor = G_GetFloorItemFromPos(pos);
150 /* nothing on the ground yet? */
151 if (!floor)
152 floor = G_SpawnFloor(pos);
153
154 Item item(od);
155 return game.invi.tryAddToInventory(&floor->chr.inv, &item, INVDEF(CID_FLOOR));
156}
157
160/* #define ADJACENT */
161
162#ifdef ADJACENT
163static bool G_InventoryPlaceItemAdjacent (Edict* ent)
164{
165 vec2_t oldPos; /* if we have to place it to adjacent */
166 int i;
167
168 Vector2Copy(ent->pos, oldPos);
169 Edict* floorAdjacent = nullptr;
170
171 for (i = 0; i < DIRECTIONS; i++) {
173 /* extend pos with the direction vectors */
176 Vector2Set(ent->pos, ent->pos[0] + dvecs[i][0], ent->pos[0] + dvecs[i][1]);
177 /* now try to get a floor entity for that new location */
178 floorAdjacent = G_GetFloorItems(ent);
179 if (!floorAdjacent) {
180 floorAdjacent = G_SpawnFloor(ent->pos);
181 } else {
182 /* destroy this edict (send this event to all clients that see the edict) */
183 G_EventPerish(*floorAdjacent);
184 G_VisFlagsReset(*floorAdjacent);
185 }
186
187 Item* ic = nullptr;
188 int x, y;
189 floorAdjacent->chr.inv.findSpace(INVDEF(CID_FLOOR), ic, &x, &y, ic);
190 if (x != NONE) {
191 ic->setX(x);
192 ic->setY(y);
193 ic->setNext(floorAdjacent->getFloor());
194 floorAdjacent->chr.inv.setFloorContainer(ic);
195 break;
196 }
197 /* restore original pos */
198 Vector2Copy(oldPos, ent->pos);
199 }
200
201 /* added to adjacent pos? */
202 if (i < DIRECTIONS) {
203 /* restore original pos - if no free space, this was done
204 * already in the for loop */
205 Vector2Copy(oldPos, ent->pos);
206 return false;
207 }
208
209 if (floorAdjacent)
210 G_CheckVis(floorAdjacent, true);
211
212 return true;
213}
214#endif
215
222{
223 /* check for items that should be dropped */
224 /* ignore items linked from any temp container */
225 const Container* cont = nullptr;
226 while ((cont = ent->chr.inv.getNextCont(cont))) {
227 if (G_InventoryDropToFloorCheck(ent, cont->id))
228 break; /* found at least one item */
229 }
230
231 /* edict is not carrying any items */
232 if (!cont)
233 return;
234
235 /* find the floor */
236 Edict* floor = G_GetFloorItems(ent);
237 if (!floor) {
238 floor = G_SpawnFloor(ent->pos);
239 } else {
240 /* destroy this edict (send this event to all clients that see the edict) */
241 G_EventPerish(*floor);
242 G_VisFlagsReset(*floor);
243 }
244
245 /* drop items */
246 /* cycle through all containers */
247 for (containerIndex_t container = 0; container < CID_MAX; container++) {
248 /* skip floor - we want to drop to floor */
249 if (container == CID_FLOOR)
250 continue;
251
252 /* skip CID_ARMOUR, we will collect armours using armour container,
253 * not CID_FLOOR */
254 if (container == CID_ARMOUR || container == CID_IMPLANT)
255 continue;
256
257 /* now cycle through all items for the container of the character (or the entity) */
258 Item* ic, *next;
259 for (ic = ent->getContainer(container); ic; ic = next) {
260 /* Save the next inv-list before it gets overwritten below.
261 * Do not put this in the "for" statement,
262 * unless you want an endless loop. ;) */
263 next = ic->getNext();
264 Item item = *ic;
265
266 /* only floor can summarize, so everything on the actor must have amount=1 */
267 assert(item.getAmount() == 1);
268 if (!game.invi.removeFromInventory(&ent->chr.inv, INVDEF(container), ic))
269 gi.Error("Could not remove item '%s' from inventory %i of entity %i",
270 ic->def()->id, container, ent->getIdNum());
271 G_EventInventoryDelete(*ent, G_VisToPM(ent->visflags), container, ic->getX(), ic->getY());
272 if (game.invi.addToInventory(&floor->chr.inv, &item, INVDEF(CID_FLOOR), NONE, NONE, 1) == nullptr)
273 gi.Error("Could not add item '%s' from inventory %i of entity %i to floor container",
274 ic->def()->id, container, ent->getIdNum());
275#ifdef ADJACENT
276 G_InventoryPlaceItemAdjacent(ent);
277#endif
278 }
279 /* destroy link */
280 ent->resetContainer(container);
281 }
282
283 ent->setFloor(floor);
284
285 /* send item info to the clients */
286 G_CheckVis(floor);
287}
288
297void G_ReadItem (Item* item, const invDef_t** container, int* x, int* y)
298{
299 int t, m;
300 int ammoleft;
301 int amount;
302 containerIndex_t containerID;
303
304 gi.ReadFormat("sbsbbbbs", &t, &ammoleft, &m, &containerID, x, y, &item->rotated, &amount);
305 item->setAmmoLeft(ammoleft);
306 item->setAmount(amount);
307
308 if (t < 0 || t >= gi.csi->numODs)
309 gi.Error("Item index out of bounds: %i", t);
310 item->setDef(&gi.csi->ods[t]);
311
312 if (m != NONE) {
313 if (m < 0 || m >= gi.csi->numODs)
314 gi.Error("Ammo index out of bounds: %i", m);
315 item->setAmmoDef(&gi.csi->ods[m]);
316 } else {
317 item->setAmmoDef(nullptr);
318 }
319
320 if (isValidContId(containerID))
321 *container = INVDEF(containerID);
322 else
323 gi.Error("container id is out of bounds: %i", containerID);
324}
325
334void G_WriteItem (const Item& item, const containerIndex_t contId, int x, int y)
335{
336 assert(item.def());
337 gi.WriteFormat("sbsbbbbs", item.def()->idx, item.getAmmoLeft(), item.ammoDef() ? item.ammoDef()->idx : NONE, contId, x, y, item.rotated, item.getAmount());
338}
339
347void G_SendInventory (playermask_t playerMask, const Edict& ent)
348{
349 int nr = 0;
350
351 /* test for pointless player mask */
352 if (!playerMask)
353 return;
354
355 const Container* cont = nullptr;
356 while ((cont = ent.chr.inv.getNextCont(cont, true))) {
357 if (!G_IsItem(&ent) && INVDEF(cont->id)->temp)
358 continue;
359 nr += cont->countItems();
360 }
361
362 /* return if no inventory items to send */
363 if (nr == 0)
364 return;
365
366 G_EventInventoryAdd(ent, playerMask, nr);
367 cont = nullptr;
368 while ((cont = ent.chr.inv.getNextCont(cont, true))) {
369 if (!G_IsItem(&ent) && INVDEF(cont->id)->temp)
370 continue;
371 const Item* item = nullptr;
372 while ((item = cont->getNextItem(item))) {
373 /* send a single item */
374 assert(item->def());
375 G_WriteItem(*item, cont->id, item->getX(), item->getY());
376 }
377 }
378 G_EventEnd();
379}
#define INVDEF(containerID)
Definition cl_shared.h:48
int countItems() const
Count the number of items in the Container.
Item * getNextItem(const Item *prev) const
teammask_t visflags
Definition g_edict.h:82
character_t chr
Definition g_edict.h:116
int getIdNum() const
Definition g_edict.h:231
void setFloor(const Edict *other)
Definition g_edict.h:207
pos3_t pos
Definition g_edict.h:55
void resetContainer(const containerIndex_t idx)
Definition g_edict.h:204
void resetFloor()
Definition g_edict.h:210
Item * getContainer(const containerIndex_t idx) const
Definition g_edict.h:243
Item * getFloor() const
Definition g_edict.h:262
void findSpace(const invDef_t *container, const Item *item, int *const px, int *const py, const Item *ignoredItem) const
Finds space for item in inv at container.
void setFloorContainer(Item *cont)
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
item instance data, with linked list capability
Definition inv_shared.h:402
const objDef_t * ammoDef(void) const
Definition inv_shared.h:460
int getAmmoLeft() const
Definition inv_shared.h:466
int getX() const
Definition inv_shared.h:454
void setAmount(int value)
Definition inv_shared.h:438
void setAmmoDef(const objDef_t *od)
Definition inv_shared.h:435
int rotated
Definition inv_shared.h:412
int getAmount() const
Definition inv_shared.h:463
void setDef(const objDef_t *objDef)
Definition inv_shared.h:444
const objDef_t * def(void) const
Definition inv_shared.h:469
void setY(const int val)
Definition inv_shared.h:432
int getY() const
Definition inv_shared.h:457
void setX(const int val)
Definition inv_shared.h:429
void setNext(Item *nx)
Definition inv_shared.h:426
void setAmmoLeft(int value)
Definition inv_shared.h:441
Item * getNext() const
Definition inv_shared.h:451
#define NONE
Definition defines.h:68
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition g_client.cpp:186
Interface for g_client.cpp.
void G_EventEnd(void)
Definition g_events.cpp:711
void G_EventInventoryDelete(const Edict &ent, playermask_t playerMask, const containerIndex_t containerId, int x, int y)
Tell the client to remove the item from the container.
Definition g_events.cpp:131
void G_EventInventoryAdd(const Edict &ent, playermask_t playerMask, int itemAmount)
Tell the client to add the item from the container.
Definition g_events.cpp:147
void G_EventPerish(const Edict &ent)
Send an event to all clients that are seeing the given edict, that it just has disappeared.
Definition g_events.cpp:158
unsigned int playermask_t
Definition g_events.h:34
void G_InventoryToFloor(Edict *ent)
Move items to adjacent locations if the containers on the current floor edict are full.
bool G_InventoryRemoveItemByID(const char *itemID, Edict *ent, containerIndex_t container)
Removes one particular item from a given container.
static bool G_InventoryDropToFloorCheck(Edict *ent, containerIndex_t container)
Checks whether the given container contains items that should be dropped to the floor....
Edict * G_GetFloorItems(Edict *ent)
Prepares a list of items on the floor at given entity position.
bool G_AddItemToFloor(const pos3_t pos, const char *itemID)
Adds a new item to an existing or new floor container edict at the given grid location.
Edict * G_GetFloorItemFromPos(const pos3_t pos)
Callback to G_GetEdictFromPos() for given position, used to get items from position.
void G_WriteItem(const Item &item, const containerIndex_t contId, int x, int y)
Write an item to the network buffer.
const equipDef_t * G_GetEquipDefByID(const char *equipID)
void G_SendInventory(playermask_t playerMask, const Edict &ent)
Sends whole inventory through the network buffer.
void G_ReadItem(Item *item, const invDef_t **container, int *x, int *y)
Read item from the network buffer.
#define G_IsItem(ent)
Definition g_local.h:134
game_locals_t game
Definition g_main.cpp:37
game_import_t gi
Definition g_main.cpp:39
Edict * G_SpawnFloor(const pos3_t pos)
Spawns a new entity at the floor.
Definition g_spawn.cpp:535
Brings new objects into the world.
Edict * G_GetEdictFromPos(const pos3_t pos, const entity_type_t type)
Searches an edict of the given type at the given grid location.
Definition g_utils.cpp:59
Misc utility functions for game module.
void G_VisFlagsReset(Edict &ent)
Definition g_vis.cpp:438
void G_CheckVis(Edict *check, const vischeckflags_t visFlags)
Check if the edict appears/perishes for the other teams. If they appear for other teams,...
Definition g_vis.cpp:409
const objDef_t * INVSH_GetItemByIDSilent(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn't found.
#define CID_IMPLANT
Definition inv_shared.h:49
#define CID_MAX
Definition inv_shared.h:57
#define CID_ARMOUR
Definition inv_shared.h:54
int32_t containerIndex_t
Definition inv_shared.h:46
#define CID_FLOOR
Definition inv_shared.h:55
bool isValidContId(const containerIndex_t id)
Definition inv_shared.h:59
const vec4_t dvecs[PATHFINDING_DIRECTIONS]
Definition mathlib.cpp:58
#define DIRECTIONS
Number of angles from a position (2-dimensional).
Definition mathlib.h:78
static struct mdfour * m
Definition md4.cpp:35
@ ET_ITEM
Definition q_shared.h:149
QGL_EXTERN GLint i
Definition r_gl.h:113
#define Q_streq(a, b)
Definition shared.h:136
Inventory inv
Definition chr_shared.h:411
char id[MAX_VAR]
Definition inv_shared.h:606
inventory definition for our menus
Definition inv_shared.h:371
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
bool isVirtual
Definition inv_shared.h:284
const char * id
Definition inv_shared.h:268
pos_t pos3_t[3]
Definition ufotypes.h:58
vec_t vec2_t[2]
Definition ufotypes.h:38
static int oldPos
#define Vector2Set(v, x, y)
Definition vector.h:61
#define Vector2Copy(src, dest)
Definition vector.h:52