Bug Summary

File:client/cgame/campaign/cp_fightequip_callbacks.cpp
Location:line 834, column 7
Description:Access to field 'item' results in a dereference of a null pointer (loaded from variable 'slot')

Annotated Source Code

1/**
2 * @file
3 * @brief Header file for menu callback functions used for base and aircraft equip menu
4 */
5
6/*
7All original material Copyright (C) 2002-2011 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24*/
25#include "../../cl_shared.h"
26#include "../../ui/ui_dataids.h"
27#include "cp_campaign.h"
28#include "cp_fightequip_callbacks.h"
29#include "cp_mapfightequip.h"
30#include "cp_ufo.h"
31
32static aircraftItemType_t airequipID = MAX_ACITEMS; /**< value of aircraftItemType_t that defines what item we are installing. */
33
34static int airequipSelectedZone = ZONE_NONE; /**< Selected zone in equip menu */
35static int airequipSelectedSlot = ZONE_NONE; /**< Selected slot in equip menu */
36static technology_t *aimSelectedTechnology = NULL__null; /**< Selected technology in equip menu */
37
38/**
39 * @brief Check airequipID value and set the correct values for aircraft items
40 */
41static void AIM_CheckAirequipID (void)
42{
43 switch (airequipID) {
44 case AC_ITEM_AMMO:
45 case AC_ITEM_WEAPON:
46 case AC_ITEM_SHIELD:
47 case AC_ITEM_ELECTRONICS:
48 return;
49 default:
50 airequipID = AC_ITEM_WEAPON;
51 break;
52 }
53}
54
55/**
56 * @brief Check that airequipSelectedSlot is the indice of an existing slot in the aircraft
57 * @note airequipSelectedSlot concerns only weapons and electronics
58 * @sa aircraft Pointer to the aircraft
59 */
60static void AIM_CheckAirequipSelectedSlot (const aircraft_t *aircraft)
61{
62 switch (airequipID) {
63 case AC_ITEM_AMMO:
64 case AC_ITEM_WEAPON:
65 if (airequipSelectedSlot >= aircraft->maxWeapons) {
66 airequipSelectedSlot = 0;
67 return;
68 }
69 break;
70 case AC_ITEM_ELECTRONICS:
71 if (airequipSelectedSlot >= aircraft->maxElectronics) {
72 airequipSelectedSlot = 0;
73 return;
74 }
75 break;
76 default:
77 break;
78 }
79}
80
81/**
82 * @brief Returns a pointer to the selected slot.
83 * @param[in] aircraft Pointer to the aircraft
84 * @param[in] type Type of slot we want to select
85 * @note also used by BDEF_ functions
86 * @return Pointer to the slot corresponding to airequipID
87 */
88aircraftSlot_t *AII_SelectAircraftSlot (aircraft_t *aircraft, aircraftItemType_t type)
89{
90 aircraftSlot_t *slot;
91
92 AIM_CheckAirequipSelectedSlot(aircraft);
93 switch (type) {
94 case AC_ITEM_SHIELD:
95 slot = &aircraft->shield;
96 break;
97 case AC_ITEM_ELECTRONICS:
98 slot = aircraft->electronics + airequipSelectedSlot;
99 break;
100 case AC_ITEM_AMMO:
101 case AC_ITEM_WEAPON:
102 slot = aircraft->weapons + airequipSelectedSlot;
103 break;
104 default:
105 Com_Printf("AII_SelectAircraftSlot: Unknown airequipID: %i\n", type);
106 return NULL__null;
107 }
108
109 return slot;
110}
111
112/**
113 * @brief Check that airequipSelectedZone is available
114 * @sa slot Pointer to the slot
115 */
116static void AIM_CheckAirequipSelectedZone (aircraftSlot_t *slot)
117{
118 assert(slot)(__builtin_expect(!(slot), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 118, "slot") : (void)0)
;
119
120 if (airequipSelectedZone == ZONE_AMMO && airequipID < AC_ITEM_AMMO && airequipID > AC_ITEM_WEAPON) {
121 /* you can select ammo only for weapons and ammo */
122 airequipSelectedZone = ZONE_MAIN;
123 }
124
125 /* You can choose an ammo only if a weapon has already been selected */
126 if (airequipID >= AC_ITEM_AMMO && !slot->item) {
127 airequipSelectedZone = ZONE_MAIN;
128 }
129}
130
131/**
132 * @brief Returns the userfriendly name for craftitem types (shown in aircraft equip menu)
133 */
134static inline const char *AIM_AircraftItemtypeName (const int equiptype)
135{
136 switch (equiptype) {
137 case AC_ITEM_WEAPON:
138 return _("Weapons")gettext("Weapons");
139 case AC_ITEM_SHIELD:
140 return _("Armour")gettext("Armour");
141 case AC_ITEM_ELECTRONICS:
142 return _("Items")gettext("Items");
143 case AC_ITEM_AMMO:
144 /* ammo - only available from weapons */
145 return _("Ammo")gettext("Ammo");
146 default:
147 return _("Unknown")gettext("Unknown");
148 }
149}
150
151/**
152 * @return @c true if the technology is available and matches the filter
153 */
154static bool AIM_CrafttypeFilter (const base_t *base, aircraftItemType_t filterType, const technology_t *tech)
155{
156 const objDef_t *item;
157 if (!base)
158 return false;
159
160 if (!RS_IsResearched_ptr(tech))
161 return false;
162
163 item = INVSH_GetItemByID(tech->provides);
164 if (!item)
165 return false;
166 if (item->isVirtual)
167 return false;
168 if (!B_BaseHasItem(base, item))
169 return false;
170
171 /* filter by type: special case for ammo because more than 1 type is an ammo type */
172 if (filterType != AC_ITEM_AMMO) {
173 if (item->craftitem.type != filterType)
174 return false;
175 } else {
176 if (item->craftitem.type < AC_ITEM_AMMO)
177 return false;
178 }
179
180 /* you can't install an item that does not have an installation time (alien item)
181 * except for ammo which does not have installation time */
182 if (item->craftitem.installationTime == -1 && filterType >= AC_ITEM_AMMO)
183 return false;
184
185 return true;
186}
187
188/**
189 * @brief Update the list of item you can choose
190 * @param[in] slot Pointer to aircraftSlot where items can be equiped
191 */
192static void AIM_UpdateAircraftItemList (const aircraftSlot_t *slot)
193{
194 linkedList_t *amountList = NULL__null;
195 technology_t **techList;
196 technology_t **currentTech;
197 const base_t *base = slot->aircraft->homebase;
198 int count = 0;
199 uiNode_t *AIM_items = NULL__null;
200
201 /* Add all items corresponding to airequipID to list */
202 techList = AII_GetCraftitemTechsByType(airequipID);
203
204 /* Count only those which are researched to buffer */
205 currentTech = techList;
206 while (*currentTech) {
207 if (AIM_CrafttypeFilter(base, airequipID, *currentTech))
208 count++;
209 currentTech++;
210 }
211
212 /* List only those which are researched to buffer */
213 currentTech = techList;
214 while (*currentTech) {
215 if (AIM_CrafttypeFilter(base, airequipID, *currentTech)) {
216 uiNode_t *option;
217 const objDef_t *item = INVSH_GetItemByID((*currentTech)->provides);
218 const int amount = B_ItemInBase(item, base);
219
220 LIST_AddString(&amountList, va("%d", amount));
221 option = cgi->UI_AddOption(&AIM_items, (*currentTech)->name, _((*currentTech)->name)gettext((*currentTech)->name), va("%d", (*currentTech)->idx));
222 if (!AIM_SelectableCraftItem(slot, *currentTech))
223 option->disabled = true;
224 }
225 currentTech++;
226 }
227
228 cgi->UI_RegisterOption(TEXT_LIST, AIM_items);
229 cgi->UI_RegisterLinkedListText(TEXT_LIST2, amountList);
230}
231
232/**
233 * @brief Draw only slots existing for this aircraft, and emphases selected one
234 * @return[out] aircraft Pointer to the aircraft
235 */
236static void AIM_DrawAircraftSlots (const aircraft_t *aircraft)
237{
238 int i;
239
240 /* initialise model cvars */
241 for (i = 0; i < AIR_POSITIONS_MAX; i++)
242 Cvar_Set(va("mn_aircraft_item_model_slot%i", i), "");
243
244 for (i = 0; i < AIR_POSITIONS_MAX; i++) {
245 const aircraftSlot_t *slot;
246 int max, j;
247
248 /* Default value */
249 cgi->UI_ExecuteConfunc("airequip_display_slot %i 0", i);
250
251 /* Draw available slots */
252 switch (airequipID) {
253 case AC_ITEM_AMMO:
254 case AC_ITEM_WEAPON:
255 max = aircraft->maxWeapons;
256 slot = aircraft->weapons;
257 break;
258 case AC_ITEM_ELECTRONICS:
259 max = aircraft->maxElectronics;
260 slot = aircraft->electronics;
261 break;
262 /* do nothing for shield: there is only one slot */
263 default:
264 continue;
265 }
266 for (j = 0; j < max; j++, slot++) {
267 /* check if one of the aircraft slots is at this position */
268 if (slot->pos == i) {
269 /* draw in white if this is the selected slot */
270 if (j == airequipSelectedSlot) {
271 cgi->UI_ExecuteConfunc("airequip_display_slot %i 2", i);
272 } else {
273 cgi->UI_ExecuteConfunc("airequip_display_slot %i 1", i);
274 }
275 if (slot->item) {
276 Cvar_Set(va("mn_aircraft_item_model_slot%i", i), RS_GetTechForItem(slot->item)->mdl);
277 } else
278 Cvar_Set(va("mn_aircraft_item_model_slot%i", i), "");
279 }
280 }
281 }
282}
283
284/**
285 * @brief Write in red the text in zone ammo (zone 2)
286 * @sa AIM_NoEmphazeAmmoSlotText
287 * @note This is intended to show the player that there is no ammo in his aircraft
288 */
289static inline void AIM_EmphazeAmmoSlotText (void)
290{
291 cgi->UI_ExecuteConfunc("airequip_zone2_color \"1 0 0 1\"");
292}
293
294/**
295 * @brief Write in white the text in zone ammo (zone 2)
296 * @sa AIM_EmphazeAmmoSlotText
297 * @note This is intended to revert effects of AIM_EmphazeAmmoSlotText
298 */
299static inline void AIM_NoEmphazeAmmoSlotText (void)
300{
301 cgi->UI_ExecuteConfunc("airequip_zone2_color \"1 1 1 1\"");
302}
303
304static void AIM_AircraftEquipMenuUpdate (void)
305{
306 static char smallbuffer1[256];
307 static char smallbuffer2[128];
308 const char *typeName;
309 aircraft_t *aircraft;
310 aircraftSlot_t *slot;
311 base_t *base = B_GetCurrentSelectedBase();
312
313 if (!base)
314 return;
315
316 /* don't let old links appear on this menu */
317 cgi->UI_ResetData(TEXT_AIREQUIP_1);
318 cgi->UI_ResetData(TEXT_AIREQUIP_2);
319 cgi->UI_ResetData(TEXT_ITEMDESCRIPTION);
320 cgi->UI_ResetData(TEXT_LIST);
321
322 aircraft = base->aircraftCurrent;
323
324 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 324, "aircraft") : (void)0)
;
325
326 /* Check that airequipSelectedSlot corresponds to an existing slot for this aircraft */
327 AIM_CheckAirequipSelectedSlot(aircraft);
328
329 /* Select slot */
330 slot = AII_SelectAircraftSlot(aircraft, airequipID);
331
332 /* Check that the selected zone is OK */
333 AIM_CheckAirequipSelectedZone(slot);
334
335 /* Fill the list of item you can equip your aircraft with */
336 AIM_UpdateAircraftItemList(slot);
337
338 Cvar_Set("mn_equip_itemtype_name", AIM_AircraftItemtypeName(airequipID));
339 switch (airequipID) {
340 case AC_ITEM_ELECTRONICS:
341 typeName = "item";
342 break;
343 case AC_ITEM_SHIELD:
344 typeName = "armour";
345 break;
346 case AC_ITEM_AMMO:
347 typeName = "ammo";
348 break;
349 case AC_ITEM_WEAPON:
350 typeName = "weapon";
351 break;
352 default:
353 typeName = "unknown";
354 break;
355 }
356 Cvar_Set("mn_equip_itemtype", typeName);
357
358 /* First slot: item currently assigned */
359 if (!slot->item) {
360 Com_sprintf(smallbuffer1, sizeof(smallbuffer1), "%s", _("No item assigned.\n")gettext("No item assigned.\n"));
361 Q_strcat(smallbuffer1, va(_("This slot is for %s or smaller items.")gettext("This slot is for %s or smaller items."),
362 AII_WeightToName(slot->size)), sizeof(smallbuffer1));
363 } else {
364 technology_t *itemTech = RS_GetTechForItem(slot->item);
365 technology_t *nextItemTech = slot->nextItem ? RS_GetTechForItem(slot->nextItem) : NULL__null;
366 /* Print next item if we are removing item currently installed and a new item has been added. */
367 Com_sprintf(smallbuffer1, sizeof(smallbuffer1), "%s\n", slot->nextItem ? _(nextItemTech->name)gettext(nextItemTech->name) : _(itemTech->name)gettext(itemTech->name));
368 if (!slot->installationTime) {
369 Q_strcat(smallbuffer1, _("This item is functional.\n")gettext("This item is functional.\n"), sizeof(smallbuffer1));
370 } else if (slot->installationTime > 0) {
371 Q_strcat(smallbuffer1, va(_("This item will be installed in %i hours.\n")gettext("This item will be installed in %i hours.\n"),
372 slot->installationTime), sizeof(smallbuffer1));
373 } else if (slot->nextItem) {
374 Q_strcat(smallbuffer1, va(_("%s will be removed in %i hours.\n")gettext("%s will be removed in %i hours.\n"), _(itemTech->name)gettext(itemTech->name),
375 - slot->installationTime), sizeof(smallbuffer1));
376 Q_strcat(smallbuffer1, va(_("%s will be installed in %i hours.\n")gettext("%s will be installed in %i hours.\n"), _(nextItemTech->name)gettext(nextItemTech->name),
377 slot->nextItem->craftitem.installationTime - slot->installationTime), sizeof(smallbuffer1));
378 } else {
379 Q_strcat(smallbuffer1, va(_("This item will be removed in %i hours.\n")gettext("This item will be removed in %i hours.\n"),
380 -slot->installationTime), sizeof(smallbuffer1));
381 }
382 }
383 cgi->UI_RegisterText(TEXT_AIREQUIP_1, smallbuffer1);
384
385 /* Second slot: ammo slot (only used for weapons) */
386 if ((airequipID == AC_ITEM_WEAPON || airequipID == AC_ITEM_AMMO) && slot->item) {
387 if (!slot->ammo) {
388 AIM_EmphazeAmmoSlotText();
389 Com_sprintf(smallbuffer2, sizeof(smallbuffer2), "%s", _("No ammo assigned to this weapon.")gettext("No ammo assigned to this weapon."));
390 } else {
391 const objDef_t *ammo = slot->nextAmmo ? slot->nextAmmo : slot->ammo;
392 const technology_t *tech = RS_GetTechForItem(ammo);
393 AIM_NoEmphazeAmmoSlotText();
394 if (!ammo->isVirtual)
395 Q_strncpyz(smallbuffer2, _(tech->name), sizeof(smallbuffer2))Q_strncpyzDebug( smallbuffer2, gettext(tech->name), sizeof
(smallbuffer2), "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 395 )
;
396 else
397 Q_strncpyz(smallbuffer2, _("No ammo needed"), sizeof(smallbuffer2))Q_strncpyzDebug( smallbuffer2, gettext("No ammo needed"), sizeof
(smallbuffer2), "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 397 )
;
398 }
399 } else
400 *smallbuffer2 = '\0';
401
402 cgi->UI_RegisterText(TEXT_AIREQUIP_2, smallbuffer2);
403
404 /* Draw existing slots for this aircraft */
405 AIM_DrawAircraftSlots(aircraft);
406}
407
408#define AIM_LOADING_OK0 0
409#define AIM_LOADING_NOSLOTSELECTED1 1
410#define AIM_LOADING_NOTECHNOLOGYSELECTED2 2
411#define AIM_LOADING_ALIENTECH3 3
412#define AIM_LOADING_TECHNOLOGYNOTRESEARCHED4 4
413#define AIM_LOADING_TOOHEAVY5 5
414#define AIM_LOADING_UNKNOWNPROBLEM6 6
415#define AIM_LOADING_NOWEAPON7 7
416#define AIM_LOADING_NOTUSABLEWITHWEAPON8 8
417
418/**
419 * @todo It is a generic function, we can move it into cp_mapfightequip.c
420 * @param[in] slot Pointer to an aircraft slot (can be base/installation too)
421 * @param[in] tech Pointer to the technology to test
422 * @return The status of the technology versus the slot
423 */
424static int AIM_CheckTechnologyIntoSlot (const aircraftSlot_t *slot, const technology_t *tech)
425{
426 const objDef_t *item;
427
428 if (!tech)
429 return AIM_LOADING_NOTECHNOLOGYSELECTED2;
430
431 if (!slot)
432 return AIM_LOADING_NOSLOTSELECTED1;
433
434 if (!RS_IsResearched_ptr(tech))
435 return AIM_LOADING_TECHNOLOGYNOTRESEARCHED4;
436
437 item = INVSH_GetItemByID(tech->provides);
438 if (!item)
439 return AIM_LOADING_NOTECHNOLOGYSELECTED2;
440
441 if (item->craftitem.type >= AC_ITEM_AMMO) {
442 const objDef_t *weapon = slot->item;
443 int k;
444 if (slot->nextItem != NULL__null)
445 weapon = slot->nextItem;
446
447 if (weapon == NULL__null)
448 return AIM_LOADING_NOWEAPON7;
449
450 /* Is the ammo is usable with the slot */
451 for (k = 0; k < weapon->numAmmos; k++) {
452 const objDef_t *usable = weapon->ammos[k];
453 if (usable && item->idx == usable->idx)
454 break;
455 }
456 if (k >= weapon->numAmmos)
457 return AIM_LOADING_NOTUSABLEWITHWEAPON8;
458
459#if 0
460 /** @todo This only works for ammo that is useable in exactly one weapon
461 * check the weap_idx array and not only the first value */
462 if (!slot->nextItem && item->weapons[0] != slot->item)
463 return AIM_LOADING_UNKNOWNPROBLEM6;
464
465 /* are we trying to change ammos for nextItem? */
466 if (slot->nextItem && item->weapons[0] != slot->nextItem)
467 return AIM_LOADING_UNKNOWNPROBLEM6;
468#endif
469 }
470
471 /* you can install an item only if its weight is small enough for the slot */
472 if (AII_GetItemWeightBySize(item) > slot->size)
473 return AIM_LOADING_TOOHEAVY5;
474
475 /* you can't install an item that you don't possess
476 * virtual ammo don't need to be possessed
477 * installations always have weapon and ammo */
478 if (slot->aircraft) {
479 if (!B_BaseHasItem(slot->aircraft->homebase, item))
480 return AIM_LOADING_UNKNOWNPROBLEM6;
481 } else if (slot->base) {
482 if (!B_BaseHasItem(slot->base, item))
483 return AIM_LOADING_UNKNOWNPROBLEM6;
484 }
485
486 /* you can't install an item that does not have an installation time (alien item)
487 * except for ammo which does not have installation time */
488 if (item->craftitem.installationTime == -1 && slot->type < AC_ITEM_AMMO)
489 return AIM_LOADING_ALIENTECH3;
490
491 return AIM_LOADING_OK0;
492}
493
494/**
495 * @brief Update the item description according to the tech and the slot selected
496 */
497static void AIM_UpdateItemDescription (bool fromList, bool fromSlot)
498{
499 int status;
500 aircraft_t *aircraft;
501 aircraftSlot_t *slot;
502 base_t *base = B_GetCurrentSelectedBase();
503 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 503, "base") : (void)0)
;
504
505 aircraft = base->aircraftCurrent;
506 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 506, "aircraft") : (void)0)
;
507 slot = AII_SelectAircraftSlot(aircraft, airequipID);
508
509 /* update mini ufopedia */
510 /** @todo we should clone the text, and not using the ufopedia text */
511 if (fromList)
512 UP_AircraftItemDescription(INVSH_GetItemByIDSilent(aimSelectedTechnology ? aimSelectedTechnology->provides : NULL__null));
513 else if (fromSlot) {
514 if (airequipID == AC_ITEM_AMMO)
515 UP_AircraftItemDescription(slot->ammo);
516 else
517 UP_AircraftItemDescription(slot->item);
518 }
519
520 /* update status */
521 status = AIM_CheckTechnologyIntoSlot(slot, aimSelectedTechnology);
522 switch (status) {
523 case AIM_LOADING_NOSLOTSELECTED1:
524 Cvar_Set("mn_aircraft_item_warning", _("No slot selected.")gettext("No slot selected."));
525 break;
526 case AIM_LOADING_NOTECHNOLOGYSELECTED2:
527 Cvar_Set("mn_aircraft_item_warning", _("No item selected.")gettext("No item selected."));
528 break;
529 case AIM_LOADING_ALIENTECH3:
530 Cvar_Set("mn_aircraft_item_warning", _("You can't equip an alien technology.")gettext("You can't equip an alien technology."));
531 break;
532 case AIM_LOADING_TECHNOLOGYNOTRESEARCHED4:
533 Cvar_Set("mn_aircraft_item_warning", _("Technology requested is not yet completed.")gettext("Technology requested is not yet completed."));
534 break;
535 case AIM_LOADING_TOOHEAVY5:
536 Cvar_Set("mn_aircraft_item_warning", _("This item is too heavy for the selected slot.")gettext("This item is too heavy for the selected slot."));
537 break;
538 case AIM_LOADING_NOWEAPON7:
539 Cvar_Set("mn_aircraft_item_warning", _("Equip a weapon first.")gettext("Equip a weapon first."));
540 break;
541 case AIM_LOADING_NOTUSABLEWITHWEAPON8:
542 Cvar_Set("mn_aircraft_item_warning", _("Ammo not usable with current weapon.")gettext("Ammo not usable with current weapon."));
543 break;
544 case AIM_LOADING_UNKNOWNPROBLEM6:
545 Cvar_Set("mn_aircraft_item_warning", _("Unknown problem.")gettext("Unknown problem."));
546 break;
547 case AIM_LOADING_OK0:
548 Cvar_Set("mn_aircraft_item_warning", _("Ok")gettext("Ok"));
549 break;
550 }
551
552 if (*Cvar_GetString("mn_item") == '\0') {
553 cgi->UI_ExecuteConfunc("airequip_no_item");
554 } else {
555 if (fromSlot) {
556 cgi->UI_ExecuteConfunc("airequip_installed_item");
557 } else {
558 if (status == AIM_LOADING_OK0)
559 cgi->UI_ExecuteConfunc("airequip_installable_item");
560 else
561 cgi->UI_ExecuteConfunc("airequip_noinstallable_item");
562 }
563 }
564}
565
566/**
567 * @brief Fills the weapon and shield list of the aircraft equip menu
568 * @sa AIM_AircraftEquipMenuClick_f
569 */
570static void AIM_AircraftEquipMenuUpdate_f (void)
571{
572 if (Cmd_Argc() != 2) {
573 if (airequipID == MAX_ACITEMS) {
574 Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
575 return;
576 }
577 AIM_CheckAirequipID();
578 } else {
579 const aircraftItemType_t type = (aircraftItemType_t)atoi(Cmd_Argv(1));
580 switch (type) {
581 case AC_ITEM_ELECTRONICS:
582 case AC_ITEM_SHIELD:
583 airequipID = type;
584 cgi->UI_ExecuteConfunc("airequip_zone2_off");
585 break;
586 case AC_ITEM_AMMO:
587 case AC_ITEM_WEAPON:
588 airequipID = type;
589 cgi->UI_ExecuteConfunc("airequip_zone2_on");
590 break;
591 default:
592 airequipID = AC_ITEM_WEAPON;
593 break;
594 }
595 }
596
597 AIM_AircraftEquipMenuUpdate();
598}
599
600/**
601 * @brief Select the current slot you want to assign the item to.
602 * @note This function is only for aircraft and not far bases.
603 */
604static void AIM_AircraftEquipSlotSelect_f (void)
605{
606 int i;
607 itemPos_t pos;
608 aircraft_t *aircraft;
609 base_t *base = B_GetCurrentSelectedBase();
610 int updateZone = 0;
611
612 if (!base)
613 return;
614
615 if (Cmd_Argc() < 2) {
616 Com_Printf("Usage: %s <arg> <zone1|zone2|item>\n", Cmd_Argv(0));
617 return;
618 }
619
620 aircraft = base->aircraftCurrent;
621 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 621, "aircraft") : (void)0)
;
622
623 i = atoi(Cmd_Argv(1));
624 pos = (itemPos_t)i;
625
626 if (Cmd_Argc() == 3) {
627 if (Q_streq(Cmd_Argv(2), "zone1")(strcmp(Cmd_Argv(2), "zone1") == 0)) {
628 updateZone = 1;
629 } else if (Q_streq(Cmd_Argv(2), "zone2")(strcmp(Cmd_Argv(2), "zone2") == 0)) {
630 updateZone = 2;
631 }
632 }
633
634 airequipSelectedSlot = ZONE_NONE;
635
636 /* select the slot corresponding to pos, and set airequipSelectedSlot to this slot */
637 switch (airequipID) {
638 case AC_ITEM_ELECTRONICS:
639 /* electronics selected */
640 for (i = 0; i < aircraft->maxElectronics; i++) {
641 if (aircraft->electronics[i].pos == pos) {
642 airequipSelectedSlot = i;
643 break;
644 }
645 }
646 if (i == aircraft->maxElectronics)
647 Com_Printf("this slot hasn't been found in aircraft electronics slots\n");
648 break;
649 case AC_ITEM_AMMO:
650 case AC_ITEM_WEAPON:
651 /* weapon selected */
652 for (i = 0; i < aircraft->maxWeapons; i++) {
653 if (aircraft->weapons[i].pos == pos) {
654 airequipSelectedSlot = i;
655 break;
656 }
657 }
658 if (i == aircraft->maxWeapons)
659 Com_Printf("this slot hasn't been found in aircraft weapon slots\n");
660 break;
661 default:
662 Com_Printf("AIM_AircraftEquipSlotSelect_f : only weapons and electronics have several slots\n");
663 break;
664 }
665
666 /* Update menu after changing slot */
667 AIM_AircraftEquipMenuUpdate();
668
669 /* update description with the selected slot */
670 if (updateZone > 0)
671 AIM_UpdateItemDescription(false, true);
672 else
673 AIM_UpdateItemDescription(true, false);
674}
675
676/**
677 * @brief Select the current zone you want to assign the item to.
678 */
679static void AIM_AircraftEquipZoneSelect_f (void)
680{
681 int zone;
682 aircraft_t *aircraft;
683 aircraftSlot_t *slot;
684 base_t *base = B_GetCurrentSelectedBase();
685
686 if (!base)
687 return;
688
689 if (Cmd_Argc() < 2) {
690 Com_Printf("Usage: %s <arg>\n", Cmd_Argv(0));
691 return;
692 }
693
694 zone = atoi(Cmd_Argv(1));
695
696 aircraft = base->aircraftCurrent;
697 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 697, "aircraft") : (void)0)
;
698 /* Select slot */
699 slot = AII_SelectAircraftSlot(aircraft, airequipID);
700
701 /* ammos are only available for weapons */
702 switch (airequipID) {
703 /* a weapon was selected - select ammo type corresponding to this weapon */
704 case AC_ITEM_WEAPON:
705 if (zone == ZONE_AMMO) {
706 if (slot->item)
707 airequipID = AC_ITEM_AMMO;
708 }
709 break;
710 /* an ammo was selected - select weapon type corresponding to this ammo */
711 case AC_ITEM_AMMO:
712 if (zone != ZONE_AMMO)
713 airequipID = AC_ITEM_WEAPON;
714 break;
715 default :
716 /* ZONE_AMMO is not available for electronics and shields */
717 if (zone == ZONE_AMMO)
718 return;
719 }
720 airequipSelectedZone = zone;
721
722 /* update menu */
723 AIM_AircraftEquipMenuUpdate();
724 /* Check that the selected zone is OK */
725 AIM_CheckAirequipSelectedZone(slot);
726
727 AIM_UpdateItemDescription(false, true);
728}
729
730/**
731 * @brief Add selected item to current zone.
732 * @note Called from airequip menu
733 * @sa aircraftItemType_t
734 */
735static void AIM_AircraftEquipAddItem_f (void)
736{
737 int zone;
738 aircraftSlot_t *slot;
739 aircraft_t *aircraft = NULL__null;
740 base_t *base = B_GetCurrentSelectedBase();
741
742 zone = (airequipID == AC_ITEM_AMMO) ? 2 : 1;
743
744 /* proceed only if an item has been selected */
745 if (!aimSelectedTechnology)
746 return;
747
748 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 748, "base") : (void)0)
;
749 aircraft = base->aircraftCurrent;
750 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 750, "aircraft") : (void)0)
;
751 base = aircraft->homebase; /* we need to know where items will be removed */
752 slot = AII_SelectAircraftSlot(aircraft, airequipID);
753
754 /* the clicked button doesn't correspond to the selected zone */
755 if (zone != airequipSelectedZone)
756 return;
757
758 /* check if the zone exists */
759 if (zone >= ZONE_MAX)
760 return;
761
762 /* update the new item to slot */
763
764 switch (zone) {
765 case ZONE_MAIN:
766 if (!slot->nextItem) {
767 /* we add the weapon, shield, item if slot is free or the installation of current item just began */
768 if (!slot->item || (slot->item && slot->installationTime == slot->item->craftitem.installationTime)) {
769 AII_RemoveItemFromSlot(base, slot, false);
770 AII_AddItemToSlot(base, aimSelectedTechnology, slot, false); /* Aircraft stats are updated below */
771 AII_AutoAddAmmo(slot);
772 break;
773 } else if (slot->item == INVSH_GetItemByID(aimSelectedTechnology->provides)) {
774 /* the added item is the same than the one in current slot */
775 if (slot->installationTime == -slot->item->craftitem.installationTime) {
776 /* player changed his mind: he just want to re-add the item he just removed */
777 slot->installationTime = 0;
778 break;
779 } else if (!slot->installationTime) {
780 /* player try to add a weapon he already have: just skip */
781 return;
782 }
783 } else {
784 /* We start removing current item in slot, and the selected item will be installed afterwards */
785 slot->installationTime = -slot->item->craftitem.installationTime;
786 /* more below */
787 }
788 } else {
789 /* remove weapon and ammo of next item */
790 AII_RemoveNextItemFromSlot(base, slot, false);
791 /* more below */
792 }
793
794 /* we change the weapon, shield, item, or base defence that will be installed AFTER the removal
795 * of the one in the slot atm */
796 AII_AddItemToSlot(base, aimSelectedTechnology, slot, true);
797 AII_AutoAddAmmo(slot);
798 break;
799 case ZONE_AMMO:
800 /* we can change ammo only if the selected item is an ammo (for weapon or base defence system) */
801 if (airequipID >= AC_ITEM_AMMO) {
802 AII_AddAmmoToSlot(base, aimSelectedTechnology, slot);
803 }
804 break;
805 default:
806 /* Zone higher than ZONE_AMMO shouldn't exist */
807 return;
808 }
809
810 /* Update the values of aircraft stats (just in case an item has an installationTime of 0) */
811 AII_UpdateAircraftStats(aircraft);
812
813 AIM_AircraftEquipMenuUpdate();
814}
815
816/**
817 * @brief Delete an object from a zone.
818 */
819static void AIM_AircraftEquipRemoveItem_f (void)
820{
821 int zone;
822 aircraftSlot_t *slot;
823 aircraft_t *aircraft = NULL__null;
824 base_t *base = B_GetCurrentSelectedBase();
825
826 zone = (airequipID == AC_ITEM_AMMO) ? 2 : 1;
1
'?' condition is false
827
828 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 828, "base") : (void)0)
;
829 aircraft = base->aircraftCurrent;
830 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_fightequip_callbacks.cpp"
, 830, "aircraft") : (void)0)
;
831 slot = AII_SelectAircraftSlot(aircraft, airequipID);
832
833 /* no item in slot: nothing to remove */
834 if (!slot->item)
2
Access to field 'item' results in a dereference of a null pointer (loaded from variable 'slot')
835 return;
836
837 /* update the new item to slot */
838
839 switch (zone) {
840 case ZONE_MAIN:
841 if (!slot->nextItem) {
842 /* we change the weapon, shield, item, or base defence that is already in the slot */
843 /* if the item has been installed since less than 1 hour, you don't need time to remove it */
844 if (slot->installationTime < slot->item->craftitem.installationTime) {
845 slot->installationTime = -slot->item->craftitem.installationTime;
846 AII_RemoveItemFromSlot(base, slot, true); /* we remove only ammo, not item */
847 } else {
848 AII_RemoveItemFromSlot(base, slot, false); /* we remove weapon and ammo */
849 }
850 /* aircraft stats are updated below */
851 } else {
852 /* we change the weapon, shield, item, or base defence that will be installed AFTER the removal
853 * of the one in the slot atm */
854 AII_RemoveNextItemFromSlot(base, slot, false); /* we remove weapon and ammo */
855 /* if you canceled next item for less than 1 hour, previous item is still functional */
856 if (slot->installationTime == -slot->item->craftitem.installationTime) {
857 slot->installationTime = 0;
858 }
859 }
860 break;
861 case ZONE_AMMO:
862 /* we can change ammo only if the selected item is an ammo (for weapon or base defence system) */
863 if (airequipID >= AC_ITEM_AMMO) {
864 if (slot->nextAmmo)
865 AII_RemoveNextItemFromSlot(base, slot, true);
866 else
867 AII_RemoveItemFromSlot(base, slot, true);
868 }
869 break;
870 default:
871 /* Zone higher than ZONE_AMMO shouldn't exist */
872 return;
873 }
874
875 /* Update the values of aircraft stats */
876 AII_UpdateAircraftStats(aircraft);
877
878 AIM_AircraftEquipMenuUpdate();
879}
880
881/**
882 * @brief Set AIM_selectedTechnology to the technology of current selected aircraft item.
883 * @sa AIM_AircraftEquipMenuUpdate_f
884 */
885static void AIM_AircraftEquipMenuClick_f (void)
886{
887 int techIdx;
888
889 if (Cmd_Argc() < 2) {
890 Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
891 return;
892 }
893
894 /* Which tech? */
895 techIdx = atoi(Cmd_Argv(1));
896 aimSelectedTechnology = RS_GetTechByIDX(techIdx);
897 AIM_UpdateItemDescription(true, false);
898}
899
900/**
901 * @brief Update the GUI with a named itemtype
902 */
903static void AIM_AircraftItemtypeByName_f (void)
904{
905 aircraftItemType_t i;
906 const char *name;
907
908 if (Cmd_Argc() != 2) {
909 Com_Printf("Usage: %s <weapon|ammo|armour|item>\n", Cmd_Argv(0));
910 return;
911 }
912
913 name = Cmd_Argv(1);
914
915 if (Q_streq(name, "weapon")(strcmp(name, "weapon") == 0))
916 i = AC_ITEM_WEAPON;
917 else if (Q_streq(name, "ammo")(strcmp(name, "ammo") == 0))
918 i = AC_ITEM_AMMO;
919 else if (Q_streq(name, "armour")(strcmp(name, "armour") == 0))
920 i = AC_ITEM_SHIELD;
921 else if (Q_streq(name, "item")(strcmp(name, "item") == 0))
922 i = AC_ITEM_ELECTRONICS;
923 else {
924 Com_Printf("AIM_AircraftItemtypeByName_f: Invalid itemtype!\n");
925 return;
926 }
927
928 airequipID = i;
929 Cmd_ExecuteString(va("airequip_updatemenu %d", airequipID));
930}
931
932void AIM_InitCallbacks (void)
933{
934 Cmd_AddCommand("airequip_updatemenu", AIM_AircraftEquipMenuUpdate_f, "Init function for the aircraft equip menu");
935 Cmd_AddCommand("airequip_selectcategory", AIM_AircraftItemtypeByName_f, "Select an item category and update the GUI");
936 Cmd_AddCommand("airequip_list_click", AIM_AircraftEquipMenuClick_f);
937 Cmd_AddCommand("airequip_slot_select", AIM_AircraftEquipSlotSelect_f);
938 Cmd_AddCommand("airequip_add_item", AIM_AircraftEquipAddItem_f, "Add item to slot");
939 Cmd_AddCommand("airequip_remove_item", AIM_AircraftEquipRemoveItem_f, "Remove item from slot");
940 Cmd_AddCommand("airequip_zone_select", AIM_AircraftEquipZoneSelect_f);
941}
942
943void AIM_ShutdownCallbacks (void)
944{
945 Cmd_RemoveCommand("airequip_updatemenu");
946 Cmd_RemoveCommand("airequip_selectcategory");
947 Cmd_RemoveCommand("airequip_list_click");
948 Cmd_RemoveCommand("airequip_slot_select");
949 Cmd_RemoveCommand("airequip_add_item");
950 Cmd_RemoveCommand("airequip_remove_item");
951 Cmd_RemoveCommand("airequip_zone_select");
952}