Bug Summary

File:client/ui/node/ui_node_container.cpp
Location:line 568, column 6
Description:Access to field 'single' results in a dereference of a null pointer (loaded from field 'container')

Annotated Source Code

1/**
2 * @file
3 * @brief The container node refer to 3 different nodes merged into a singler one. Both
4 * can drag and drop soldier items from a container to another one. The first container
5 * is a soldier slot. For example, the left arm, the bag pack... The second is the base
6 * inventiory (which is now an extended node from container). And the last it a floor
7 * container used into the battlescape. The node name itself is used to know the container
8 * role.
9 * @todo Move container role outside of the node name
10 * @todo Link soldier container with a soldier
11 * @todo Link floor container with a map/cell...
12 */
13
14/*
15Copyright (C) 2002-2011 UFO: Alien Invasion.
16
17This program is free software; you can redistribute it and/or
18modify it under the terms of the GNU General Public License
19as published by the Free Software Foundation; either version 2
20of the License, or (at your option) any later version.
21
22This program is distributed in the hope that it will be useful,
23but WITHOUT ANY WARRANTY; without even the implied warranty of
24MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25
26See the GNU General Public License for more details.
27
28You should have received a copy of the GNU General Public License
29along with this program; if not, write to the Free Software
30Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31
32*/
33
34#include "../ui_main.h"
35#include "../ui_parse.h"
36#include "../ui_behaviour.h"
37#include "../ui_actions.h"
38#include "../ui_dragndrop.h"
39#include "../ui_tooltip.h"
40#include "../ui_nodes.h"
41#include "../ui_input.h"
42#include "../ui_render.h"
43#include "ui_node_model.h"
44#include "ui_node_container.h"
45#include "ui_node_abstractnode.h"
46
47#include "../../client.h"
48#include "../../renderer/r_draw.h"
49#include "../../renderer/r_mesh.h"
50#include "../../cgame/cl_game.h"
51#include "../../battlescape/cl_actor.h"
52#include "../../cl_inventory.h"
53
54/**
55 * @todo need refactoring to remove, reduce use... of that var
56 * Global access to many node content like that is very bad.
57 */
58inventory_t *ui_inventory = NULL__null;
59
60#define EXTRADATA_TYPEcontainerExtraData_t containerExtraData_t
61#define EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))) UI_EXTRADATA(node, EXTRADATA_TYPE)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t))))
62#define EXTRADATACONST(node)(*((const containerExtraData_t*)((const char*)node + sizeof(uiNode_t
))))
UI_EXTRADATACONST(node, EXTRADATA_TYPE)(*((const containerExtraData_t*)((const char*)node + sizeof(uiNode_t
))))
63
64/**
65 * self cache for drag item
66 * @note we can use a global variable because we only can have 1 source node at a time
67 */
68static int dragInfoFromX = -1;
69static int dragInfoFromY = -1;
70
71/**
72 * self cache for the preview and dropped item
73 * @note we can use a global variable because we only can have 1 target node at a time
74 */
75static int dragInfoToX = -1;
76static int dragInfoToY = -1;
77
78/**
79 * The current invList pointer (only used for ignoring the dragged item
80 * for finding free space right now)
81 */
82static const invList_t *dragInfoIC;
83
84/**
85 * @brief Searches if there is an item at location (x/y) in a scrollable container. You can also provide an item to search for directly (x/y is ignored in that case).
86 * @note x = x-th item in a row, y = row. i.e. x/y does not equal the "grid" coordinates as used in those containers.
87 * @param[in] container The container to get the item from
88 * @param[in] item Item requested
89 * @param[in] filterType Filter used.
90 * @todo Remove filter it is not a generic concept, and here it mean nothing
91 * @return invList_t Pointer to the invList_t/item that is located at x/y or equals "item".
92 * @sa INVSH_SearchInInventory
93 */
94static invList_t *UI_ContainerNodeGetExistingItem (const invDef_t *container, const objDef_t *item, const itemFilterTypes_t filterType)
95{
96 return INV_SearchInInventoryWithFilter(ui_inventory, container, item, filterType);
97}
98
99static inline bool UI_IsScrollContainerNode (const uiNode_t* const node)
100{
101 return EXTRADATACONST(node)(*((const containerExtraData_t*)((const char*)node + sizeof(uiNode_t
))))
.container && EXTRADATACONST(node)(*((const containerExtraData_t*)((const char*)node + sizeof(uiNode_t
))))
.container->scroll;
102}
103
104/**
105 * @brief Fills the ground container of the ui_inventory with unused items from a given
106 * equipment definition
107 * @note Keep in mind that @c ed is changed here - so items are removed and the ground container
108 * of a inventory definition is in general a temp container - that means you should make a copy
109 * of the @c equipDef_t you want to add to the temp ground container of the given @c inv
110 * @todo it's not obvious for the caller that @c ui_inventory pointer must be set
111 * @param[in,out] inv The inventory to add the unused items from @c ed to
112 * @param[in,out] ed The equipment definition to get the used items from that should be added
113 * to the ground container of @c inv
114 * @todo not used nor called by the container node; should be move somewhere else
115 */
116void UI_ContainerNodeUpdateEquipment (inventory_t *inv, const equipDef_t *ed)
117{
118 int i;
119 int *const numItems = Mem_Dup(int, ed->numItems, lengthof(ed->numItems))(int*)_Mem_PoolDup(1 ? (ed->numItems) : (int*)0 , sizeof(int
) * ((sizeof(ed->numItems) / sizeof(*(ed->numItems)))),
com_genericPool, 0, "src/client/ui/node/ui_node_container.cpp"
, 119)
;
120
121 /* a 'tiny hack' to add the remaining equipment (not carried)
122 * correctly into buy categories, reloading at the same time;
123 * it is valid only due to the following property: */
124 assert(MAX_CONTAINERS >= FILTER_AIRCRAFT)(__builtin_expect(!(16 >= FILTER_AIRCRAFT), 0) ? __assert_rtn
(__func__, "src/client/ui/node/ui_node_container.cpp", 124, "MAX_CONTAINERS >= FILTER_AIRCRAFT"
) : (void)0)
;
125
126 for (i = 0; i < csi.numODs; i++) {
127 const objDef_t *od = INVSH_GetItemByIDX(i);
128 /* Don't allow to show unuseable items. */
129 if (!GAME_ItemIsUseable(od))
130 continue;
131
132 while (numItems[i]) {
133 const item_t item = {NONE_AMMO0, NULL__null, od, 0, 0};
134 if (!cls.i.AddToInventory(&cls.i, inv, &item, INVDEF(csi.idEquip)(&csi.ids[(csi.idEquip)]), NONE-1, NONE-1, 1)) {
135 /* no space left in the inventory */
136 break;
137 }
138 numItems[item.t->idx]--;
139 }
140 }
141
142 Mem_Free(numItems)_Mem_Free((numItems),"src/client/ui/node/ui_node_container.cpp"
,142)
;
143
144 /* First-time linking of ui_inventory. */
145 if (ui_inventory && !ui_inventory->c[csi.idEquip]) {
146 ui_inventory->c[csi.idEquip] = inv->c[csi.idEquip];
147 }
148}
149
150/**
151 * @brief Draws an item to the screen
152 * @param[in] node Context node
153 * @param[in] org Node position on the screen (pixel). Single nodes: Use the center of the node.
154 * @param[in] item The item to draw.
155 * @param[in] x Position in container. Set this to -1 if it's drawn in a single container.
156 * @param[in] y Position in container. Set this to -1 if it's drawn in a single container.
157 * @param[in] scale
158 * @param[in] color
159 * @sa SCR_DrawCursor
160 * Used to draw an item to the equipment containers. First look whether the objDef_t
161 * includes an image - if there is none then draw the model
162 */
163void UI_DrawItem (uiNode_t *node, const vec3_t org, const item_t *item, int x, int y, const vec3_t scale, const vec4_t color)
164{
165 const objDef_t *od = item->t;
166 vec4_t col;
167 vec3_t origin;
168
169 assert(od)(__builtin_expect(!(od), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 169, "od") : (void)0)
;
170 assert(org[2] > -1000 && org[2] < 1000)(__builtin_expect(!(org[2] > -1000 && org[2] < 1000
), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 170, "org[2] > -1000 && org[2] < 1000") : (void
)0)
; /*< prevent use of vec2_t for org */
171
172 Vector4Copy(color, col)((col)[0]=(color)[0],(col)[1]=(color)[1],(col)[2]=(color)[2],
(col)[3]=(color)[3])
;
173 /* no ammo in this weapon - highlight this item */
174 if (od->weapon && od->reload && !item->a) {
175 col[1] *= 0.5;
176 col[2] *= 0.5;
177 }
178
179 VectorCopy(org, origin)((origin)[0]=(org)[0],(origin)[1]=(org)[1],(origin)[2]=(org)[
2])
;
180
181 /* Calculate correct location of the image or the model (depends on rotation) */
182 /** @todo Change the rotation of the image as well, right now only the location is changed.
183 * How is image-rotation handled right now? */
184 if (x >= 0 || y >= 0) {
185 /* Add offset of location in container. */
186 origin[0] += x * C_UNIT25;
187 origin[1] += y * C_UNIT25;
188
189 /* Add offset for item-center (depends on rotation). */
190 if (item->rotated) {
191 origin[0] += od->sy * C_UNIT25 / 2.0;
192 origin[1] += od->sx * C_UNIT25 / 2.0;
193 /** @todo Image size calculation depends on handling of image-rotation.
194 imgWidth = od->sy * C_UNIT;
195 imgHeight = od->sx * C_UNIT;
196 */
197 } else {
198 origin[0] += od->sx * C_UNIT25 / 2.0;
199 origin[1] += od->sy * C_UNIT25 / 2.0;
200 }
201 }
202
203 /* don't handle the od->tech->image here - it's very ufopedia specific in most cases */
204 if (od->image[0] != '\0') {
205 const int imgWidth = od->sx * C_UNIT25;
206 const int imgHeight = od->sy * C_UNIT25;
207 origin[0] -= od->sx * C_UNIT25 / 2.0;
208 origin[1] -= od->sy * C_UNIT25 / 2.0;
209
210 /* Draw the image. */
211 R_Color(color);
212 UI_DrawNormImageByName(false, origin[0], origin[1], imgWidth, imgHeight, 0, 0, 0, 0, od->image);
213 R_Color(NULL__null);
214 } else {
215 uiModel_t *model = NULL__null;
216 const char *modelName = GAME_GetModelForItem(od, &model);
217
218 /* no model definition in the tech struct, not in the fallback object definition */
219 if (Q_strnull(modelName)((modelName) == __null || (modelName)[0] == '\0')) {
220 Com_Printf("UI_DrawItem: No model given for item: '%s'\n", od->id);
221 return;
222 }
223
224 if (model && node) {
225 UI_DrawModelNode(node, modelName);
226 } else {
227 modelInfo_t mi;
228 vec3_t angles = {-10, 160, 70};
229 vec3_t size = {scale[0], scale[1], scale[2]};
230 vec3_t center;
231
232 if (item->rotated)
233 angles[0] -= 90;
234
235 if (od->scale)
236 VectorScale(size, od->scale, size)((size)[0] = (size)[0] * (od->scale),(size)[1] = (size)[1]
* (od->scale),(size)[2] = (size)[2] * (od->scale))
;
237
238 VectorNegate(od->center, center)((center)[0]=-(od->center)[0],(center)[1]=-(od->center)
[1],(center)[2]=-(od->center)[2])
;
239
240 OBJZERO(mi)(memset(&((mi)), (0), sizeof((mi))));
241 mi.origin = origin;
242 mi.angles = angles;
243 mi.center = center;
244 mi.scale = size;
245 mi.color = col;
246 mi.name = modelName;
247
248 /* draw the model */
249 R_DrawModelDirect(&mi, NULL__null, NULL__null);
250 }
251 }
252}
253
254/**
255 * @brief Generate tooltip text for an item.
256 * @param[in] item The item we want to generate the tooltip text for.
257 * @param[in,out] tooltipText Pointer to a string the information should be written into.
258 * @param[in] stringMaxLength Max. string size of @c tooltipText.
259 * @return Number of lines
260 */
261static void UI_GetItemTooltip (item_t item, char *tooltipText, size_t stringMaxLength)
262{
263 const objDef_t *weapon;
264
265 assert(item.t)(__builtin_expect(!(item.t), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 265, "item.t") : (void)0)
;
266
267 if (item.amount > 1)
268 Com_sprintf(tooltipText, stringMaxLength, "%i x %s\n", item.amount, _(item.t->name)gettext(item.t->name));
269 else
270 Com_sprintf(tooltipText, stringMaxLength, "%s\n", _(item.t->name)gettext(item.t->name));
271
272 /* Only display further info if item.t is researched */
273 if (GAME_ItemIsUseable(item.t)) {
274 if (item.t->weapon) {
275 /* Get info about used ammo (if there is any) */
276 if (item.t == item.m) {
277 /* Item has no ammo but might have shot-count */
278 if (item.a) {
279 Q_strcat(tooltipText, va(_("Ammo: %i\n")gettext("Ammo: %i\n"), item.a), stringMaxLength);
280 }
281 } else if (item.m) {
282 /* Search for used ammo and display name + ammo count */
283 Q_strcat(tooltipText, va(_("%s loaded\n")gettext("%s loaded\n"), _(item.m->name)gettext(item.m->name)), stringMaxLength);
284 Q_strcat(tooltipText, va(_("Ammo: %i\n")gettext("Ammo: %i\n"), item.a), stringMaxLength);
285 }
286 } else if (item.t->numWeapons) {
287 /* Check if this is a non-weapon and non-ammo item */
288 if (!(item.t->numWeapons == 1 && item.t->weapons[0] == item.t)) {
289 int i;
290 /* If it's ammo get the weapon names it can be used in */
291 Q_strcat(tooltipText, _("Usable in:\n")gettext("Usable in:\n"), stringMaxLength);
292 for (i = 0; i < item.t->numWeapons; i++) {
293 weapon = item.t->weapons[i];
294 if (GAME_ItemIsUseable(weapon)) {
295 Q_strcat(tooltipText, va("* %s\n", _(weapon->name)gettext(weapon->name)), stringMaxLength);
296 }
297 }
298 }
299 }
300 }
301}
302
303/**
304 * @brief Search a child container node by the given container id
305 * @note Only search with one depth
306 */
307uiNode_t *UI_GetContainerNodeByContainerIDX (const uiNode_t* const parent, const int index)
308{
309 const invDef_t* const container = INVDEF(index)(&csi.ids[(index)]);
310 uiNode_t *containerNode = UI_GetNode(parent, container->name);
311 return containerNode;
312}
313
314/**
315 * @brief Draws the rectangle in a 'free' style on position posx/posy (pixel) in the size sizex/sizey (pixel)
316 */
317static void UI_DrawDisabled (const uiNode_t* node)
318{
319 const vec4_t color = { 0.3f, 0.3f, 0.3f, 0.7f };
320 vec2_t nodepos;
321
322 UI_GetNodeAbsPos(node, nodepos);
323 UI_DrawFill(nodepos[0], nodepos[1], node->box.size[0], node->box.size[1], color);
324}
325
326/**
327 * @brief Draws the rectangle in a 'free' style on position posx/posy (pixel) in the size sizex/sizey (pixel)
328 */
329static void UI_DrawFree (containerIndex_t container, const uiNode_t *node, int posx, int posy, int sizex, int sizey, bool showTUs)
330{
331 const vec4_t color = { 0.0f, 1.0f, 0.0f, 0.7f };
332 invDef_t* inv = INVDEF(container)(&csi.ids[(container)]);
333 vec2_t nodepos;
334
335 UI_GetNodeAbsPos(node, nodepos);
336 UI_DrawFill(posx, posy, sizex, sizey, color);
337
338 /* if showTUs is true (only the first time in none single containers)
339 * and we are connected to a game */
340 if (showTUs && CL_BattlescapeRunning()) {
341 UI_DrawString("f_verysmall", ALIGN_UL, nodepos[0] + 3, nodepos[1] + 3,
342 nodepos[0] + 3, node->box.size[0] - 6, 0,
343 va(_("In: %i Out: %i")gettext("In: %i Out: %i"), inv->in, inv->out));
344 }
345}
346
347/**
348 * @brief Draws the free and usable inventory positions when dragging an item.
349 * @note Only call this function in dragging mode
350 */
351static void UI_ContainerNodeDrawFreeSpace (uiNode_t *node, inventory_t *inv)
352{
353 const objDef_t *od = UI_DNDGetItem()->t; /**< Get the 'type' of the dragged item. */
354 vec2_t nodepos;
355
356 /* Draw only in dragging-mode and not for the equip-floor */
357 assert(UI_DNDIsDragging())(__builtin_expect(!(UI_DNDIsDragging()), 0) ? __assert_rtn(__func__
, "src/client/ui/node/ui_node_container.cpp", 357, "UI_DNDIsDragging()"
) : (void)0)
;
358 assert(inv)(__builtin_expect(!(inv), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 358, "inv") : (void)0)
;
359
360 UI_GetNodeAbsPos(node, nodepos);
361 /* if single container (hands, extension, headgear) */
362 if (EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->single) {
363 /* if container is free or the dragged-item is in it */
364 if (UI_DNDIsSourceNode(node) || INVSH_CheckToInventory(inv, od, EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container, 0, 0, dragInfoIC))
365 UI_DrawFree(EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->id, node, nodepos[0], nodepos[1], node->box.size[0], node->box.size[1], true);
366 } else {
367 /* The shape of the free positions. */
368 uint32_t free[SHAPE_BIG_MAX_HEIGHT16];
369 bool showTUs = true;
370 int x, y;
371
372 OBJZERO(free)(memset(&((free)), (0), sizeof((free))));
373
374 for (y = 0; y < SHAPE_BIG_MAX_HEIGHT16; y++) {
375 for (x = 0; x < SHAPE_BIG_MAX_WIDTH32; x++) {
376 /* Check if the current position is usable (topleft of the item). */
377
378 /* Add '1's to each position the item is 'blocking'. */
379 const int checkedTo = INVSH_CheckToInventory(inv, od, EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container, x, y, dragInfoIC);
380 if (checkedTo & INV_FITS) /* Item can be placed normally. */
381 INVSH_MergeShapes(free, (uint32_t)od->shape, x, y);
382 if (checkedTo & INV_FITS_ONLY_ROTATED) /* Item can be placed rotated. */
383 INVSH_MergeShapes(free, INVSH_ShapeRotate((uint32_t)od->shape), x, y);
384
385 /* Only draw on existing positions. */
386 if (INVSH_CheckShape(EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->shape, x, y)) {
387 if (INVSH_CheckShape(free, x, y)) {
388 UI_DrawFree(EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->id, node, nodepos[0] + x * C_UNIT25, nodepos[1] + y * C_UNIT25, C_UNIT25, C_UNIT25, showTUs);
389 showTUs = false;
390 }
391 }
392 }
393 }
394 }
395}
396
397/**
398 * @brief Calculates the size of a container node and links the container
399 * into the node (uses the @c invDef_t shape bitmask to determine the size)
400 * @param[in,out] node The node to get the size for
401 */
402void uiContainerNode::onLoaded (uiNode_t* const node)
403{
404 const char *name;
405 const invDef_t *container;
406
407 /** @todo find a better way to add more equip node, without this hack */
408 name = node->name;
409 if (Q_strstart(node->name, "equip_"))
410 name = "equip";
411
412 container = INVSH_GetInventoryDefinitionByID(name);
413 if (container == NULL__null)
414 return;
415
416 EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container = container;
417
418 if (UI_IsScrollContainerNode(node)) {
419 /* No need to compute the size, the script provide it */
420 } else {
421 int i, j;
422 /* Start on the last bit of the shape mask. */
423 for (i = SHAPE_BIG_MAX_WIDTH32 - 1; i >= 0; i--) {
424 for (j = 0; j < SHAPE_BIG_MAX_HEIGHT16; j++)
425 if (container->shape[j] & (1 << i))
426 break;
427 if (j < SHAPE_BIG_MAX_HEIGHT16)
428 break;
429 }
430 node->box.size[0] = C_UNIT25 * (i + 1) + 0.01;
431
432 /* start on the lower row of the shape mask */
433 for (i = SHAPE_BIG_MAX_HEIGHT16 - 1; i >= 0; i--)
434 if (container->shape[i] & ~0x0)
435 break;
436 node->box.size[1] = C_UNIT25 * (i + 1) + 0.01;
437 }
438}
439
440static const vec3_t scale = {3.5, 3.5, 3.5};
441/** @todo it may be nice to vectorise that */
442static const vec4_t colorDefault = {1, 1, 1, 1};
443static const vec4_t colorLoadable = {0.5, 1, 0.5, 1};
444static const vec4_t colorDisabled = {0.5, 0.5, 0.5, 1};
445static const vec4_t colorDisabledLoadable = {0.5, 0.25, 0.25, 1.0};
446static const vec4_t colorPreview = { 0.5, 0.5, 1, 1 }; /**< Make the preview item look bluish */
447
448/**
449 * @brief Draw a container which only contains one item
450 * @param node Context node
451 * @param highlightType Current selected object
452 */
453static void UI_ContainerNodeDrawSingle (uiNode_t *node, const objDef_t *highlightType)
454{
455 vec4_t color;
456 vec3_t pos;
457
458 UI_GetNodeAbsPos(node, pos);
459 pos[0] += node->box.size[0] / 2.0;
460 pos[1] += node->box.size[1] / 2.0;
461 pos[2] = 0;
462
463 /* Single item container (special case for left hand). */
464 if (INV_IsLeftDef(EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container) && !ui_inventory->c[csi.idLeft]) {
465 if (ui_inventory->c[csi.idRight]) {
466 const item_t *item = &ui_inventory->c[csi.idRight]->item;
467 assert(item)(__builtin_expect(!(item), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 467, "item") : (void)0)
;
468 assert(item->t)(__builtin_expect(!(item->t), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 468, "item->t") : (void)0)
;
469
470 if (item->t->holdTwoHanded) {
471 if (highlightType && INVSH_LoadableInWeapon(highlightType, item->t))
472 memcpy(color, colorLoadable, sizeof(vec4_t));
473 else
474 memcpy(color, colorDefault, sizeof(vec4_t));
475 color[3] = 0.5;
476 UI_DrawItem(node, pos, item, -1, -1, scale, color);
477 }
478 }
479 } else if (ui_inventory->c[EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->id]) {
480 bool disabled = false;
481 const item_t *item;
482
483 if (ui_inventory->c[csi.idRight]) {
484 item = &ui_inventory->c[csi.idRight]->item;
485 /* If there is a weapon in the right hand that needs two hands to shoot it
486 * and there is a weapon in the left, then draw a disabled marker for the
487 * fireTwoHanded weapon. */
488 assert(item)(__builtin_expect(!(item), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 488, "item") : (void)0)
;
489 assert(item->t)(__builtin_expect(!(item->t), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 489, "item->t") : (void)0)
;
490 if (INV_IsRightDef(EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container) && item->t->fireTwoHanded && ui_inventory->c[csi.idLeft]) {
491 disabled = true;
492 UI_DrawDisabled(node);
493 }
494 }
495
496 item = &ui_inventory->c[EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->id]->item;
497 assert(item)(__builtin_expect(!(item), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 497, "item") : (void)0)
;
498 assert(item->t)(__builtin_expect(!(item->t), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 498, "item->t") : (void)0)
;
499 if (highlightType && INVSH_LoadableInWeapon(highlightType, item->t)) {
500 if (disabled)
501 Vector4Copy(colorDisabledLoadable, color)((color)[0]=(colorDisabledLoadable)[0],(color)[1]=(colorDisabledLoadable
)[1],(color)[2]=(colorDisabledLoadable)[2],(color)[3]=(colorDisabledLoadable
)[3])
;
502 else
503 Vector4Copy(colorLoadable, color)((color)[0]=(colorLoadable)[0],(color)[1]=(colorLoadable)[1],
(color)[2]=(colorLoadable)[2],(color)[3]=(colorLoadable)[3])
;
504 } else {
505 if (disabled)
506 Vector4Copy(colorDisabled, color)((color)[0]=(colorDisabled)[0],(color)[1]=(colorDisabled)[1],
(color)[2]=(colorDisabled)[2],(color)[3]=(colorDisabled)[3])
;
507 else
508 Vector4Copy(colorDefault, color)((color)[0]=(colorDefault)[0],(color)[1]=(colorDefault)[1],(color
)[2]=(colorDefault)[2],(color)[3]=(colorDefault)[3])
;
509 }
510 if (disabled)
511 color[3] = 0.5;
512 UI_DrawItem(node, pos, item, -1, -1, scale, color);
513 }
514}
515
516/**
517 * @brief Draw a grip container
518 */
519static void UI_ContainerNodeDrawGrid (uiNode_t *node, const objDef_t *highlightType)
520{
521 const invList_t *ic;
522 vec3_t pos;
523
524 UI_GetNodeAbsPos(node, pos);
525 pos[2] = 0;
526
527 for (ic = ui_inventory->c[EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->id]; ic; ic = ic->next) {
528 assert(ic->item.t)(__builtin_expect(!(ic->item.t), 0) ? __assert_rtn(__func__
, "src/client/ui/node/ui_node_container.cpp", 528, "ic->item.t"
) : (void)0)
;
529 if (highlightType && INVSH_LoadableInWeapon(highlightType, ic->item.t))
530 UI_DrawItem(node, pos, &ic->item, ic->x, ic->y, scale, colorLoadable);
531 else
532 UI_DrawItem(node, pos, &ic->item, ic->x, ic->y, scale, colorDefault);
533 }
534}
535
536/**
537 * @brief Draw a preview of the DND item dropped into the node
538 */
539static void UI_ContainerNodeDrawDropPreview (uiNode_t *target)
540{
541 item_t previewItem;
542 int checkedTo;
543 vec3_t origine;
544
545 /* no preview into scrollable list */
546 if (UI_IsScrollContainerNode(target))
9
Taking false branch
547 return;
548
549 /* copy the DND item to not change the original one */
550 previewItem = *UI_DNDGetItem();
551 previewItem.rotated = false;
552 checkedTo = INVSH_CheckToInventory(ui_inventory, previewItem.t, EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container, dragInfoToX, dragInfoToY, dragInfoIC);
553 if (checkedTo == INV_FITS_ONLY_ROTATED)
10
Taking false branch
554 previewItem.rotated = true;
555
556 /* no place found */
557 if (!checkedTo)
11
Taking false branch
558 return;
559
560 /* Hack, no preview for armour, we don't want it out of the armour container (and armour container is not visible) */
561 if (INV_IsArmour(previewItem.t)((strcmp((previewItem.t)->type, "armour") == 0)))
12
Taking false branch
562 return;
563
564 UI_GetNodeAbsPos(target, origine);
565 origine[2] = -40;
566
567 /* Get center of single container for placement of preview item */
568 if (EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container->single) {
13
Within the expansion of the macro 'EXTRADATA':
a
Access to field 'single' results in a dereference of a null pointer (loaded from field 'container')
569 origine[0] += target->box.size[0] / 2.0;
570 origine[1] += target->box.size[1] / 2.0;
571 /* This is a "grid" container - we need to calculate the item-position
572 * (on the screen) from stored placement in the container and the
573 * calculated rotation info. */
574 } else {
575 if (previewItem.rotated) {
576 origine[0] += (dragInfoToX + previewItem.t->sy / 2.0) * C_UNIT25;
577 origine[1] += (dragInfoToY + previewItem.t->sx / 2.0) * C_UNIT25;
578 } else {
579 origine[0] += (dragInfoToX + previewItem.t->sx / 2.0) * C_UNIT25;
580 origine[1] += (dragInfoToY + previewItem.t->sy / 2.0) * C_UNIT25;
581 }
582 }
583
584 UI_DrawItem(NULL__null, origine, &previewItem, -1, -1, scale, colorPreview);
585}
586
587/**
588 * @brief Main function to draw a container node
589 */
590void uiContainerNode::draw (uiNode_t *node)
591{
592 const objDef_t *highlightType = NULL__null;
593
594 if (!EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container)
1
Taking false branch
595 return;
596 if (!ui_inventory)
2
Taking false branch
597 return;
598 /* is container invisible */
599 if (node->color[3] < 0.001)
3
Taking false branch
600 return;
601
602 /* Highlight weapons that the dragged ammo (if it is one) can be loaded into. */
603 if (UI_DNDIsDragging() && UI_DNDGetType() == DND_ITEM) {
4
Taking false branch
604 highlightType = UI_DNDGetItem()->t;
605 }
606
607 if (EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->single) {
5
Taking false branch
608 UI_ContainerNodeDrawSingle(node, highlightType);
609 } else {
610 if (UI_IsScrollContainerNode(node)) {
6
Taking false branch
611 assert(false)(__builtin_expect(!(false), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 611, "false") : (void)0)
;
612 } else {
613 UI_ContainerNodeDrawGrid(node, highlightType);
614 }
615 }
616
617 /* Draw free space if dragging - but not for idEquip */
618 if (UI_DNDIsDragging() && EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container->id != csi.idEquip)
7
Taking false branch
619 UI_ContainerNodeDrawFreeSpace(node, ui_inventory);
620
621 if (UI_DNDIsTargetNode(node))
8
Taking true branch
622 UI_ContainerNodeDrawDropPreview(node);
623}
624
625/**
626 * @brief Gets location of the item the mouse is over
627 * @param[in] node The container-node.
628 * @param[in] mouseX X location of the mouse.
629 * @param[in] mouseY Y location of the mouse.
630 * @param[out] contX X location in the container (index of item in row).
631 * @param[out] contY Y location in the container (row).
632 * @sa UI_ContainerNodeSearchInScrollableContainer
633 */
634static invList_t *UI_ContainerNodeGetItemAtPosition (const uiNode_t* const node, int mouseX, int mouseY, int* contX, int* contY)
635{
636 invList_t *result = NULL__null;
637
638 if (!ui_inventory)
639 return NULL__null;
640
641 /* Get coordinates inside a scrollable container (if it is one). */
642 if (UI_IsScrollContainerNode(node)) {
643 Sys_Error("UI_ContainerNodeGetItemAtPosition is not usable for scrollable containers!");
644 } else {
645 vec2_t nodepos;
646 int fromX, fromY;
647
648 UI_GetNodeAbsPos(node, nodepos);
649 /* Normalize screen coordinates to container coordinates. */
650 fromX = (int) (mouseX - nodepos[0]) / C_UNIT25;
651 fromY = (int) (mouseY - nodepos[1]) / C_UNIT25;
652 if (contX)
653 *contX = fromX;
654 if (contY)
655 *contY = fromY;
656
657 result = INVSH_SearchInInventory(ui_inventory, EXTRADATACONST(node)(*((const containerExtraData_t*)((const char*)node + sizeof(uiNode_t
))))
.container, fromX, fromY);
658 }
659 return result;
660}
661
662/**
663 * @brief Custom tooltip for container node
664 * @param[in] node Node we request to draw tooltip
665 * @param[in] x Position x of the mouse
666 * @param[in] y Position y of the mouse
667 */
668void uiContainerNode::drawTooltip (uiNode_t *node, int x, int y)
669{
670 static char tooltiptext[MAX_VAR64 * 2];
671 const invList_t *itemHover;
672 vec2_t nodepos;
673
674 UI_GetNodeAbsPos(node, nodepos);
675
676 /* Find out where the mouse is. */
677 itemHover = UI_ContainerNodeGetItemAtPosition(node, x, y, NULL__null, NULL__null);
678
679 if (itemHover) {
680 const int itemToolTipWidth = 250;
681
682 /* Get name and info about item */
683 UI_GetItemTooltip(itemHover->item, tooltiptext, sizeof(tooltiptext));
684#ifdef DEBUG1
685 /* Display stored container-coordinates of the item. */
686 Q_strcat(tooltiptext, va("\n%i/%i", itemHover->x, itemHover->y), sizeof(tooltiptext));
687#endif
688 UI_DrawTooltip(tooltiptext, x, y, itemToolTipWidth);
689 }
690}
691
692static bool UI_ContainerNodeAddItem (const invDef_t *container, invList_t *ic, containerIndex_t containerID)
693{
694 int px, py;
695 const invDef_t *target = INVDEF(containerID)(&csi.ids[(containerID)]);
696 INVSH_FindSpace(ui_inventory, &ic->item, target, &px, &py, NULL__null);
697 return INV_MoveItem(ui_inventory, target, px, py, container, ic);
698}
699
700/**
701 * @brief Try to autoplace an item from a container.
702 * @param[in] node The context node
703 * @param[in] ic An item from the node container
704 * @todo We should use an item ID, and get the item inside the function, to avoid wrong uses.
705 */
706void UI_ContainerNodeAutoPlaceItem (uiNode_t* node, invList_t *ic)
707{
708 containerIndex_t target;
709 uiNode_t *targetNode;
710 bool ammoChanged = false;
711 const invDef_t *container = EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container;
712
713 /* Right click: automatic item assignment/removal. */
714 if (container->id != csi.idEquip) {
715 if (ic->item.m && ic->item.m != ic->item.t && ic->item.a) {
716 /* Remove ammo on removing weapon from a soldier */
717 target = csi.idEquip;
718 ammoChanged = INV_UnloadWeapon(ic, ui_inventory, INVDEF(target)(&csi.ids[(target)]));
719 } else {
720 /* Move back to idEquip (ground, floor) container. */
721 target = csi.idEquip;
722 INV_MoveItem(ui_inventory, INVDEF(target)(&csi.ids[(target)]), NONE-1, NONE-1, container, ic);
723 }
724 } else {
725 bool packed = false;
726 assert(ic->item.t)(__builtin_expect(!(ic->item.t), 0) ? __assert_rtn(__func__
, "src/client/ui/node/ui_node_container.cpp", 726, "ic->item.t"
) : (void)0)
;
727 /* armour can only have one target */
728 if (INV_IsArmour(ic->item.t)((strcmp((ic->item.t)->type, "armour") == 0))) {
729 target = csi.idArmour;
730 packed = INV_MoveItem(ui_inventory, INVDEF(target)(&csi.ids[(target)]), 0, 0, container, ic);
731 /* ammo or item */
732 } else if (INV_IsAmmo(ic->item.t)((strcmp((ic->item.t)->type, "ammo") == 0))) {
733 /* Finally try left and right hand. There is no other place to put it now. */
734 const containerIndex_t idxArray[] = { csi.idBelt, csi.idHolster, csi.idBackpack, csi.idLeft, csi.idRight };
735 const size_t size = lengthof(idxArray)(sizeof(idxArray) / sizeof(*(idxArray)));
736 unsigned int i;
737 for (i = 0; i < size; i++) {
738 target = idxArray[i];
739 packed = UI_ContainerNodeAddItem(container, ic, target);
740 if (packed)
741 break;
742 }
743 } else {
744 if (ic->item.t->headgear) {
745 target = csi.idHeadgear;
746 packed = UI_ContainerNodeAddItem(container, ic, target);
747 } else {
748 /* left and right are single containers, but this might change - it's cleaner to check
749 * for available space here, too */
750 const containerIndex_t idxArray[] = { csi.idRight, csi.idLeft, csi.idBelt, csi.idHolster, csi.idBackpack };
751 const size_t size = lengthof(idxArray)(sizeof(idxArray) / sizeof(*(idxArray)));
752 unsigned int i;
753 for (i = 0; i < size; i++) {
754 target = idxArray[i];
755 packed = UI_ContainerNodeAddItem(container, ic, target);
756 if (packed) {
757 if ((ic->item.t->weapon && !ic->item.a) || ic->item.t->oneshot)
758 ammoChanged = INV_LoadWeapon(ic, ui_inventory, container, INVDEF(target)(&csi.ids[(target)]));
759 break;
760 }
761 }
762 }
763 }
764 /* no need to continue here - placement wasn't successful at all */
765 if (!packed)
766 return;
767 }
768
769 /* Run onChange events */
770 targetNode = UI_GetContainerNodeByContainerIDX(node->parent, target);
771 if (node->onChange)
772 UI_ExecuteEventActions(node, node->onChange);
773 if (targetNode != NULL__null && node != targetNode && targetNode->onChange)
774 UI_ExecuteEventActions(targetNode, targetNode->onChange);
775 /* Also call onChange for equip_ammo if ammo moved
776 * Maybe there's a better way to do this? */
777 if (INV_IsAmmo(ic->item.t)((strcmp((ic->item.t)->type, "ammo") == 0)) || ammoChanged) {
778 /** @todo hard coded node name, remove it when it is possible */
779 uiNode_t *ammoNode = UI_GetNode(node->root, "equip_ammo");
780 if (ammoNode != NULL__null && node != ammoNode && ammoNode->onChange)
781 UI_ExecuteEventActions(ammoNode, ammoNode->onChange);
782 }
783}
784
785/**
786 * @brief Try to autoplace an item at a position
787 * when right-click was used in the inventory.
788 * @param[in] node The context node
789 * @param[in] mouseX X mouse coordinates.
790 * @param[in] mouseY Y mouse coordinates.
791 */
792static void UI_ContainerNodeAutoPlace (uiNode_t* node, int mouseX, int mouseY)
793{
794 int sel;
795 invList_t *ic;
796 int fromX, fromY;
797
798 if (!ui_inventory)
799 return;
800
801 /* don't allow this in tactical missions */
802 if (CL_BattlescapeRunning())
803 return;
804
805 sel = cl_selected->integer;
806 if (sel < 0)
807 return;
808
809 assert(EXTRADATA(node).container)(__builtin_expect(!((*((containerExtraData_t*)((char*)node + sizeof
(uiNode_t)))).container), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 809, "EXTRADATA(node).container") : (void)0)
;
810
811 ic = UI_ContainerNodeGetItemAtPosition(node, mouseX, mouseY, &fromX, &fromY);
812 Com_DPrintf(DEBUG_CLIENT0x20, "UI_ContainerNodeAutoPlace: item %i/%i selected from scrollable container.\n", fromX, fromY);
813 if (!ic)
814 return;
815 UI_ContainerNodeAutoPlaceItem(node, ic);
816}
817
818static int oldMouseX = 0;
819static int oldMouseY = 0;
820
821void uiContainerNode::onCapturedMouseMove (uiNode_t *node, int x, int y)
822{
823 const int delta = abs(oldMouseX - x) + abs(oldMouseY - y);
824 if (delta > 15) {
825 UI_DNDDragItem(node, &(dragInfoIC->item));
826 UI_MouseRelease();
827 }
828}
829
830void uiContainerNode::onMouseDown (uiNode_t *node, int x, int y, int button)
831{
832 switch (button) {
833 case K_MOUSE1:
834 {
835 /* start drag and drop */
836 int fromX, fromY;
837 dragInfoIC = UI_ContainerNodeGetItemAtPosition(node, x, y, &fromX, &fromY);
838 if (dragInfoIC) {
839 dragInfoFromX = fromX;
840 dragInfoFromY = fromY;
841 oldMouseX = x;
842 oldMouseY = y;
843 UI_SetMouseCapture(node);
844 EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).lastSelectedId = dragInfoIC->item.t->idx;
845 if (EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).onSelect) {
846 UI_ExecuteEventActions(node, EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).onSelect);
847 }
848 }
849 break;
850 }
851 case K_MOUSE2:
852 if (UI_DNDIsDragging()) {
853 UI_DNDAbort();
854 } else {
855 /* auto place */
856 UI_ContainerNodeAutoPlace(node, x, y);
857 }
858 break;
859 default:
860 break;
861 }
862}
863
864void uiContainerNode::onMouseUp (uiNode_t *node, int x, int y, int button)
865{
866 if (button != K_MOUSE1)
867 return;
868 if (UI_GetMouseCapture() == node) {
869 UI_MouseRelease();
870 }
871 if (UI_DNDIsDragging()) {
872 UI_DNDDrop();
873 }
874}
875
876void uiContainerNode::onLoading (uiNode_t *node)
877{
878 EXTRADATA(node)(*((containerExtraData_t*)((char*)node + sizeof(uiNode_t)))).container = NULL__null;
879 node->color[3] = 1.0;
880}
881
882/**
883 * @brief Call when a DND enter into the node
884 */
885bool uiContainerNode::onDndEnter (uiNode_t *target)
886{
887 /* accept items only, if we have a container */
888 return UI_DNDGetType() == DND_ITEM && EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container && (!UI_IsScrollContainerNode(target) || UI_DNDGetSourceNode() != target);
889}
890
891/**
892 * @brief Call into the target when the DND hover it
893 * @return True if the DND is accepted
894 */
895bool uiContainerNode::onDndMove (uiNode_t *target, int x, int y)
896{
897 vec2_t nodepos;
898 bool exists;
899 int itemX = 0;
900 int itemY = 0;
901 item_t *dragItem = UI_DNDGetItem();
902
903 /* we already check it when the node accept the DND */
904 assert(EXTRADATA(target).container)(__builtin_expect(!((*((containerExtraData_t*)((char*)target +
sizeof(uiNode_t)))).container), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 904, "EXTRADATA(target).container") : (void)0)
;
905
906 UI_GetNodeAbsPos(target, nodepos);
907
908 /** We calculate the position of the top-left corner of the dragged
909 * item in oder to compensate for the centered-drawn cursor-item.
910 * Or to be more exact, we calculate the relative offset from the cursor
911 * location to the middle of the top-left square of the item.
912 * @sa UI_LeftClick */
913 if (dragItem->t) {
914 itemX = C_UNIT25 * dragItem->t->sx / 2; /* Half item-width. */
915 itemY = C_UNIT25 * dragItem->t->sy / 2; /* Half item-height. */
916
917 /* Place relative center in the middle of the square. */
918 itemX -= C_UNIT25 / 2;
919 itemY -= C_UNIT25 / 2;
920 }
921
922 dragInfoToX = (mousePosX - nodepos[0] - itemX) / C_UNIT25;
923 dragInfoToY = (mousePosY - nodepos[1] - itemY) / C_UNIT25;
924
925 /* Check if the items already exists in the container. i.e. there is already at least one item. */
926 exists = false;
927 if ((INV_IsFloorDef(EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container) || INV_IsEquipDef(EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container))
928 && (dragInfoToX < 0 || dragInfoToY < 0 || dragInfoToX >= SHAPE_BIG_MAX_WIDTH32 || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT16)
929 && INVSH_ExistsInInventory(ui_inventory, EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container, dragItem)) {
930 exists = true;
931 }
932
933 /* Search for a suitable position to render the item at if
934 * the container is "single", the cursor is out of bound of the container. */
935 if (!exists && dragItem->t && (EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container->single
936 || dragInfoToX < 0 || dragInfoToY < 0
937 || dragInfoToX >= SHAPE_BIG_MAX_WIDTH32 || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT16)) {
938#if 0
939/* ... or there is something in the way. */
940/* We would need to check for weapon/ammo as well here, otherwise a preview would be drawn as well when hovering over the correct weapon to reload. */
941 || (INVSH_CheckToInventory(ui_inventory, dragItem->t, EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container, dragInfoToX, dragInfoToY) == INV_DOES_NOT_FIT)) {
942#endif
943 INVSH_FindSpace(ui_inventory, dragItem, EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container, &dragInfoToX, &dragInfoToY, dragInfoIC);
944 }
945
946 /* we can drag every thing */
947 if (UI_IsScrollContainerNode(target)) {
948 return true;
949 }
950
951 {
952 invList_t *fItem;
953
954 /* is there empty slot? */
955 const int checkedTo = INVSH_CheckToInventory(ui_inventory, dragItem->t, EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container, dragInfoToX, dragInfoToY, dragInfoIC);
956 if (checkedTo != INV_DOES_NOT_FIT)
957 return true;
958
959 /* can we equip dragging item into the target item? */
960 fItem = INVSH_SearchInInventory(ui_inventory, EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container, dragInfoToX, dragInfoToY);
961 if (!fItem)
962 return false;
963 if (EXTRADATA(target)(*((containerExtraData_t*)((char*)target + sizeof(uiNode_t)))
)
.container->single)
964 return true;
965 return INVSH_LoadableInWeapon(dragItem->t, fItem->item.t);
966 }
967}
968
969/**
970 * @brief Call when a DND enter into the node
971 */
972void uiContainerNode::onDndLeave (uiNode_t *node)
973{
974 dragInfoToX = -1;
975 dragInfoToY = -1;
976}
977
978/**
979 * @brief Call into the source when the DND end
980 */
981bool uiContainerNode::onDndFinished (uiNode_t *source, bool isDropped)
982{
983 item_t *dragItem = UI_DNDGetItem();
984 const invDef_t *sourceContainer = EXTRADATACONST(source)(*((const containerExtraData_t*)((const char*)source + sizeof
(uiNode_t))))
.container;
985
986 /* if the target can't finalize the DND we stop */
987 if (!isDropped) {
988 return false;
989 }
990
991 assert(sourceContainer)(__builtin_expect(!(sourceContainer), 0) ? __assert_rtn(__func__
, "src/client/ui/node/ui_node_container.cpp", 991, "sourceContainer"
) : (void)0)
;
992
993 /* on tactical mission */
994 if (CL_BattlescapeRunning()) {
995 const uiNode_t *target = UI_DNDGetTargetNode();
996 const invDef_t *targetContainer = EXTRADATACONST(target)(*((const containerExtraData_t*)((const char*)target + sizeof
(uiNode_t))))
.container;
997 assert(targetContainer)(__builtin_expect(!(targetContainer), 0) ? __assert_rtn(__func__
, "src/client/ui/node/ui_node_container.cpp", 997, "targetContainer"
) : (void)0)
;
998 CL_ActorInvMove(selActor, sourceContainer->id, dragInfoFromX, dragInfoFromY,
999 targetContainer->id, dragInfoToX, dragInfoToY);
1000 } else {
1001 uiNode_t *target = UI_DNDGetTargetNode();
1002 if (target) {
1003 invList_t *fItem;
1004 const invDef_t *targetContainer = EXTRADATACONST(target)(*((const containerExtraData_t*)((const char*)target + sizeof
(uiNode_t))))
.container;
1005 assert(targetContainer)(__builtin_expect(!(targetContainer), 0) ? __assert_rtn(__func__
, "src/client/ui/node/ui_node_container.cpp", 1005, "targetContainer"
) : (void)0)
;
1006 if (UI_IsScrollContainerNode(source)) {
1007 fItem = UI_ContainerNodeGetExistingItem(sourceContainer, dragItem->t, MAX_FILTERTYPES);
1008 } else
1009 fItem = INVSH_SearchInInventory(ui_inventory, sourceContainer, dragInfoFromX, dragInfoFromY);
1010 assert(fItem)(__builtin_expect(!(fItem), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_container.cpp"
, 1010, "fItem") : (void)0)
;
1011
1012 /** @todo We must split the move in two. Here, we should not know how to add the item to the target (see dndDrop) */
1013 /* Remove ammo on removing weapon from a soldier */
1014 if (UI_IsScrollContainerNode(target) && fItem->item.m && fItem->item.m != fItem->item.t && fItem->item.a)
1015 INV_UnloadWeapon(fItem, ui_inventory, targetContainer);
1016
1017 /* move the item */
1018 INV_MoveItem(ui_inventory, targetContainer, dragInfoToX, dragInfoToY, sourceContainer, fItem);
1019
1020 /* Add ammo on adding weapon to a soldier */
1021 if (UI_IsScrollContainerNode(source) && ((fItem->item.t->weapon && !fItem->item.a) || fItem->item.t->oneshot))
1022 INV_LoadWeapon(fItem, ui_inventory, sourceContainer, targetContainer);
1023
1024 /* Run onChange events */
1025 if (source->onChange)
1026 UI_ExecuteEventActions(source, source->onChange);
1027 if (source != target && target->onChange)
1028 UI_ExecuteEventActions(target, target->onChange);
1029 }
1030 }
1031
1032 dragInfoFromX = -1;
1033 dragInfoFromY = -1;
1034 return true;
1035}
1036
1037void UI_RegisterContainerNode (uiBehaviour_t* behaviour)
1038{
1039 behaviour->name = "container";
1040 behaviour->manager = new uiContainerNode();
1041 behaviour->extraDataSize = sizeof(EXTRADATA_TYPEcontainerExtraData_t);
1042
1043 /* Callback value set before calling onSelect. It is used to know the item selected */
1044 UI_RegisterExtradataNodeProperty(behaviour, "lastselectedid", V_INT, containerExtraData_t, lastSelectedId)UI_RegisterNodePropertyPosSize_(behaviour, "lastselectedid", V_INT
, ((size_t) &((containerExtraData_t *)(((containerExtraData_t
*)((char*)0 + sizeof(uiNode_t)))))->lastSelectedId), sizeof
(((containerExtraData_t *)0)->lastSelectedId))
;
1045 /* Callback event called when the user select an item */
1046 UI_RegisterExtradataNodeProperty(behaviour, "onselect", V_UI_ACTION, containerExtraData_t, onSelect)UI_RegisterNodePropertyPosSize_(behaviour, "onselect", (0x8000
+ 0), ((size_t) &((containerExtraData_t *)(((containerExtraData_t
*)((char*)0 + sizeof(uiNode_t)))))->onSelect), sizeof(((containerExtraData_t
*)0)->onSelect))
;
1047}