1 /**
2 * @file
3 * @brief Header file for menu callback functions used for base and aircraft equip menu
4 */
5
6 /*
7 All original material Copyright (C) 2002-2013 UFO: Alien Invasion.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, 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
32 static aircraftItemType_t airequipID = MAX_ACITEMS; /**< value of aircraftItemType_t that defines what item we are installing. */
33
34 static int airequipSelectedZone = ZONE_NONE; /**< Selected zone in equip menu */
35 static int airequipSelectedSlot = ZONE_NONE; /**< Selected slot in equip menu */
36 static technology_t* aimSelectedTechnology = nullptr; /**< Selected technology in equip menu */
37
38 /**
39 * @brief Check airequipID value and set the correct values for aircraft items
40 */
AIM_CheckAirequipID(void)41 static 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 */
AIM_CheckAirequipSelectedSlot(const aircraft_t * aircraft)60 static 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 */
AII_SelectAircraftSlot(aircraft_t * aircraft,aircraftItemType_t type)88 aircraftSlot_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 nullptr;
107 }
108
109 return slot;
110 }
111
112 /**
113 * @brief Check that airequipSelectedZone is available
114 * @sa slot Pointer to the slot
115 */
AIM_CheckAirequipSelectedZone(aircraftSlot_t * slot)116 static void AIM_CheckAirequipSelectedZone (aircraftSlot_t* slot)
117 {
118 assert(slot);
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 */
AIM_AircraftItemtypeName(const int equiptype)134 static inline const char* AIM_AircraftItemtypeName (const int equiptype)
135 {
136 switch (equiptype) {
137 case AC_ITEM_WEAPON:
138 return _("Weapons");
139 case AC_ITEM_SHIELD:
140 return _("Armour");
141 case AC_ITEM_ELECTRONICS:
142 return _("Items");
143 case AC_ITEM_AMMO:
144 /* ammo - only available from weapons */
145 return _("Ammo");
146 default:
147 return _("Unknown");
148 }
149 }
150
151 /**
152 * @return @c true if the technology is available and matches the filter
153 */
AIM_CrafttypeFilter(const base_t * base,aircraftItemType_t filterType,const technology_t * tech)154 static 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 */
AIM_UpdateAircraftItemList(const aircraftSlot_t * slot)192 static void AIM_UpdateAircraftItemList (const aircraftSlot_t* slot)
193 {
194 linkedList_t* amountList = nullptr;
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 = nullptr;
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 cgi->LIST_AddString(&amountList, va("%d", amount));
221 option = cgi->UI_AddOption(&AIM_items, (*currentTech)->name, _((*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 */
AIM_DrawAircraftSlots(const aircraft_t * aircraft)236 static 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 cgi->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 cgi->Cvar_Set(va("mn_aircraft_item_model_slot%i", i), "%s", RS_GetTechForItem(slot->item)->mdl);
277 } else {
278 cgi->Cvar_Set(va("mn_aircraft_item_model_slot%i", i), "");
279 }
280 }
281 }
282 }
283 }
284
285 /**
286 * @brief Write in red the text in zone ammo (zone 2)
287 * @sa AIM_NoEmphazeAmmoSlotText
288 * @note This is intended to show the player that there is no ammo in his aircraft
289 */
AIM_EmphazeAmmoSlotText(void)290 static inline void AIM_EmphazeAmmoSlotText (void)
291 {
292 cgi->UI_ExecuteConfunc("airequip_zone2_color \"1 0 0 1\"");
293 }
294
295 /**
296 * @brief Write in white the text in zone ammo (zone 2)
297 * @sa AIM_EmphazeAmmoSlotText
298 * @note This is intended to revert effects of AIM_EmphazeAmmoSlotText
299 */
AIM_NoEmphazeAmmoSlotText(void)300 static inline void AIM_NoEmphazeAmmoSlotText (void)
301 {
302 cgi->UI_ExecuteConfunc("airequip_zone2_color \"1 1 1 1\"");
303 }
304
AIM_AircraftEquipMenuUpdate(void)305 static void AIM_AircraftEquipMenuUpdate (void)
306 {
307 static char smallbuffer1[256];
308 static char smallbuffer2[128];
309 const char* typeName;
310 aircraft_t* aircraft;
311 aircraftSlot_t* slot;
312 base_t* base = B_GetCurrentSelectedBase();
313
314 if (!base)
315 return;
316
317 /* don't let old links appear on this menu */
318 cgi->UI_ResetData(TEXT_AIREQUIP_1);
319 cgi->UI_ResetData(TEXT_AIREQUIP_2);
320 cgi->UI_ResetData(TEXT_ITEMDESCRIPTION);
321 cgi->UI_ResetData(TEXT_LIST);
322
323 aircraft = base->aircraftCurrent;
324
325 assert(aircraft);
326
327 /* Check that airequipSelectedSlot corresponds to an existing slot for this aircraft */
328 AIM_CheckAirequipSelectedSlot(aircraft);
329
330 /* Select slot */
331 slot = AII_SelectAircraftSlot(aircraft, airequipID);
332
333 /* Check that the selected zone is OK */
334 AIM_CheckAirequipSelectedZone(slot);
335
336 /* Fill the list of item you can equip your aircraft with */
337 AIM_UpdateAircraftItemList(slot);
338
339 cgi->Cvar_Set("mn_equip_itemtype_name", "%s", AIM_AircraftItemtypeName(airequipID));
340 switch (airequipID) {
341 case AC_ITEM_ELECTRONICS:
342 typeName = "item";
343 break;
344 case AC_ITEM_SHIELD:
345 typeName = "armour";
346 break;
347 case AC_ITEM_AMMO:
348 typeName = "ammo";
349 break;
350 case AC_ITEM_WEAPON:
351 typeName = "weapon";
352 break;
353 default:
354 typeName = "unknown";
355 break;
356 }
357 cgi->Cvar_Set("mn_equip_itemtype", "%s", typeName);
358
359 /* First slot: item currently assigned */
360 if (!slot->item) {
361 Com_sprintf(smallbuffer1, sizeof(smallbuffer1), "%s", _("No item assigned.\n"));
362 Q_strcat(smallbuffer1, sizeof(smallbuffer1), _("This slot is for %s or smaller items."),
363 AII_WeightToName(slot->size));
364 } else {
365 technology_t* itemTech = RS_GetTechForItem(slot->item);
366 technology_t* nextItemTech = slot->nextItem ? RS_GetTechForItem(slot->nextItem) : nullptr;
367 /* Print next item if we are removing item currently installed and a new item has been added. */
368 Com_sprintf(smallbuffer1, sizeof(smallbuffer1), "%s\n", slot->nextItem ? _(nextItemTech->name) : _(itemTech->name));
369 if (!slot->installationTime) {
370 Q_strcat(smallbuffer1, sizeof(smallbuffer1), _("This item is functional.\n"));
371 } else if (slot->installationTime > 0) {
372 Q_strcat(smallbuffer1, sizeof(smallbuffer1), _("This item will be installed in %i hours.\n"),
373 slot->installationTime);
374 } else if (slot->nextItem) {
375 Q_strcat(smallbuffer1, sizeof(smallbuffer1), _("%s will be removed in %i hours.\n"), _(itemTech->name),
376 - slot->installationTime);
377 Q_strcat(smallbuffer1, sizeof(smallbuffer1), _("%s will be installed in %i hours.\n"), _(nextItemTech->name),
378 slot->nextItem->craftitem.installationTime - slot->installationTime);
379 } else {
380 Q_strcat(smallbuffer1, sizeof(smallbuffer1), _("This item will be removed in %i hours.\n"),
381 -slot->installationTime);
382 }
383 }
384 cgi->UI_RegisterText(TEXT_AIREQUIP_1, smallbuffer1);
385
386 /* Second slot: ammo slot (only used for weapons) */
387 if ((airequipID == AC_ITEM_WEAPON || airequipID == AC_ITEM_AMMO) && slot->item) {
388 if (!slot->ammo) {
389 AIM_EmphazeAmmoSlotText();
390 Com_sprintf(smallbuffer2, sizeof(smallbuffer2), "%s", _("No ammo assigned to this weapon."));
391 } else {
392 const objDef_t* ammo = slot->nextAmmo ? slot->nextAmmo : slot->ammo;
393 const technology_t* tech = RS_GetTechForItem(ammo);
394 AIM_NoEmphazeAmmoSlotText();
395 if (!ammo->isVirtual)
396 Q_strncpyz(smallbuffer2, _(tech->name), sizeof(smallbuffer2));
397 else
398 Q_strncpyz(smallbuffer2, _("No ammo needed"), sizeof(smallbuffer2));
399 }
400 } else
401 *smallbuffer2 = '\0';
402
403 cgi->UI_RegisterText(TEXT_AIREQUIP_2, smallbuffer2);
404
405 /* Draw existing slots for this aircraft */
406 AIM_DrawAircraftSlots(aircraft);
407 }
408
409 #define AIM_LOADING_OK 0
410 #define AIM_LOADING_NOSLOTSELECTED 1
411 #define AIM_LOADING_NOTECHNOLOGYSELECTED 2
412 #define AIM_LOADING_ALIENTECH 3
413 #define AIM_LOADING_TECHNOLOGYNOTRESEARCHED 4
414 #define AIM_LOADING_TOOHEAVY 5
415 #define AIM_LOADING_UNKNOWNPROBLEM 6
416 #define AIM_LOADING_NOWEAPON 7
417 #define AIM_LOADING_NOTUSABLEWITHWEAPON 8
418
419 /**
420 * @todo It is a generic function, we can move it into cp_mapfightequip.c
421 * @param[in] slot Pointer to an aircraft slot (can be base/installation too)
422 * @param[in] tech Pointer to the technology to test
423 * @return The status of the technology versus the slot
424 */
AIM_CheckTechnologyIntoSlot(const aircraftSlot_t * slot,const technology_t * tech)425 static int AIM_CheckTechnologyIntoSlot (const aircraftSlot_t* slot, const technology_t* tech)
426 {
427 const objDef_t* item;
428
429 if (!tech)
430 return AIM_LOADING_NOTECHNOLOGYSELECTED;
431
432 if (!slot)
433 return AIM_LOADING_NOSLOTSELECTED;
434
435 if (!RS_IsResearched_ptr(tech))
436 return AIM_LOADING_TECHNOLOGYNOTRESEARCHED;
437
438 item = INVSH_GetItemByID(tech->provides);
439 if (!item)
440 return AIM_LOADING_NOTECHNOLOGYSELECTED;
441
442 if (item->craftitem.type >= AC_ITEM_AMMO) {
443 const objDef_t* weapon = slot->item;
444 int k;
445 if (slot->nextItem != nullptr)
446 weapon = slot->nextItem;
447
448 if (weapon == nullptr)
449 return AIM_LOADING_NOWEAPON;
450
451 /* Is the ammo is usable with the slot */
452 for (k = 0; k < weapon->numAmmos; k++) {
453 const objDef_t* usable = weapon->ammos[k];
454 if (usable && item->idx == usable->idx)
455 break;
456 }
457 if (k >= weapon->numAmmos)
458 return AIM_LOADING_NOTUSABLEWITHWEAPON;
459
460 #if 0
461 /** @todo This only works for ammo that is useable in exactly one weapon
462 * check the weap_idx array and not only the first value */
463 if (!slot->nextItem && item->weapons[0] != slot->item)
464 return AIM_LOADING_UNKNOWNPROBLEM;
465
466 /* are we trying to change ammos for nextItem? */
467 if (slot->nextItem && item->weapons[0] != slot->nextItem)
468 return AIM_LOADING_UNKNOWNPROBLEM;
469 #endif
470 }
471
472 /* you can install an item only if its weight is small enough for the slot */
473 if (AII_GetItemWeightBySize(item) > slot->size)
474 return AIM_LOADING_TOOHEAVY;
475
476 /* you can't install an item that you don't possess
477 * virtual ammo don't need to be possessed
478 * installations always have weapon and ammo */
479 if (slot->aircraft) {
480 if (!B_BaseHasItem(slot->aircraft->homebase, item))
481 return AIM_LOADING_UNKNOWNPROBLEM;
482 } else if (slot->base) {
483 if (!B_BaseHasItem(slot->base, item))
484 return AIM_LOADING_UNKNOWNPROBLEM;
485 }
486
487 /* you can't install an item that does not have an installation time (alien item)
488 * except for ammo which does not have installation time */
489 if (item->craftitem.installationTime == -1 && slot->type < AC_ITEM_AMMO)
490 return AIM_LOADING_ALIENTECH;
491
492 return AIM_LOADING_OK;
493 }
494
495 /**
496 * @brief Update the item description according to the tech and the slot selected
497 */
AIM_UpdateItemDescription(bool fromList,bool fromSlot)498 static void AIM_UpdateItemDescription (bool fromList, bool fromSlot)
499 {
500 int status;
501 aircraft_t* aircraft;
502 aircraftSlot_t* slot;
503 base_t* base = B_GetCurrentSelectedBase();
504 assert(base);
505
506 aircraft = base->aircraftCurrent;
507 assert(aircraft);
508 slot = AII_SelectAircraftSlot(aircraft, airequipID);
509
510 /* update mini ufopedia */
511 /** @todo we should clone the text, and not using the ufopedia text */
512 if (fromList)
513 UP_AircraftItemDescription(INVSH_GetItemByIDSilent(aimSelectedTechnology ? aimSelectedTechnology->provides : nullptr));
514 else if (fromSlot) {
515 if (airequipID == AC_ITEM_AMMO)
516 UP_AircraftItemDescription(slot->ammo);
517 else
518 UP_AircraftItemDescription(slot->item);
519 }
520
521 /* update status */
522 status = AIM_CheckTechnologyIntoSlot(slot, aimSelectedTechnology);
523 switch (status) {
524 case AIM_LOADING_NOSLOTSELECTED:
525 cgi->Cvar_Set("mn_aircraft_item_warning", _("No slot selected."));
526 break;
527 case AIM_LOADING_NOTECHNOLOGYSELECTED:
528 cgi->Cvar_Set("mn_aircraft_item_warning", _("No item selected."));
529 break;
530 case AIM_LOADING_ALIENTECH:
531 cgi->Cvar_Set("mn_aircraft_item_warning", _("You can't equip an alien technology."));
532 break;
533 case AIM_LOADING_TECHNOLOGYNOTRESEARCHED:
534 cgi->Cvar_Set("mn_aircraft_item_warning", _("Technology requested is not yet completed."));
535 break;
536 case AIM_LOADING_TOOHEAVY:
537 cgi->Cvar_Set("mn_aircraft_item_warning", _("This item is too heavy for the selected slot."));
538 break;
539 case AIM_LOADING_NOWEAPON:
540 cgi->Cvar_Set("mn_aircraft_item_warning", _("Equip a weapon first."));
541 break;
542 case AIM_LOADING_NOTUSABLEWITHWEAPON:
543 cgi->Cvar_Set("mn_aircraft_item_warning", _("Ammo not usable with current weapon."));
544 break;
545 case AIM_LOADING_UNKNOWNPROBLEM:
546 cgi->Cvar_Set("mn_aircraft_item_warning", _("Unknown problem."));
547 break;
548 case AIM_LOADING_OK:
549 cgi->Cvar_Set("mn_aircraft_item_warning", _("Ok"));
550 break;
551 }
552
553 if (*cgi->Cvar_GetString("mn_item") == '\0') {
554 cgi->UI_ExecuteConfunc("airequip_no_item");
555 } else {
556 if (fromSlot) {
557 cgi->UI_ExecuteConfunc("airequip_installed_item");
558 } else {
559 if (status == AIM_LOADING_OK)
560 cgi->UI_ExecuteConfunc("airequip_installable_item");
561 else
562 cgi->UI_ExecuteConfunc("airequip_noinstallable_item");
563 }
564 }
565 }
566
567 /**
568 * @brief Fills the weapon and shield list of the aircraft equip menu
569 * @sa AIM_AircraftEquipMenuClick_f
570 */
AIM_AircraftEquipMenuUpdate_f(void)571 static void AIM_AircraftEquipMenuUpdate_f (void)
572 {
573 if (cgi->Cmd_Argc() != 2) {
574 if (airequipID == MAX_ACITEMS) {
575 Com_Printf("Usage: %s <num>\n", cgi->Cmd_Argv(0));
576 return;
577 }
578 AIM_CheckAirequipID();
579 } else {
580 const aircraftItemType_t type = (aircraftItemType_t)atoi(cgi->Cmd_Argv(1));
581 switch (type) {
582 case AC_ITEM_ELECTRONICS:
583 case AC_ITEM_SHIELD:
584 airequipID = type;
585 cgi->UI_ExecuteConfunc("airequip_zone2_off");
586 break;
587 case AC_ITEM_AMMO:
588 case AC_ITEM_WEAPON:
589 airequipID = type;
590 cgi->UI_ExecuteConfunc("airequip_zone2_on");
591 break;
592 default:
593 airequipID = AC_ITEM_WEAPON;
594 break;
595 }
596 }
597
598 AIM_AircraftEquipMenuUpdate();
599 }
600
601 /**
602 * @brief Select the current slot you want to assign the item to.
603 * @note This function is only for aircraft and not far bases.
604 */
AIM_AircraftEquipSlotSelect_f(void)605 static void AIM_AircraftEquipSlotSelect_f (void)
606 {
607 int i;
608 itemPos_t pos;
609 aircraft_t* aircraft;
610 base_t* base = B_GetCurrentSelectedBase();
611 int updateZone = 0;
612
613 if (!base)
614 return;
615
616 if (cgi->Cmd_Argc() < 2) {
617 Com_Printf("Usage: %s <arg> <zone1|zone2|item>\n", cgi->Cmd_Argv(0));
618 return;
619 }
620
621 aircraft = base->aircraftCurrent;
622 assert(aircraft);
623
624 i = atoi(cgi->Cmd_Argv(1));
625 pos = (itemPos_t)i;
626
627 if (cgi->Cmd_Argc() == 3) {
628 if (Q_streq(cgi->Cmd_Argv(2), "zone1")) {
629 updateZone = 1;
630 } else if (Q_streq(cgi->Cmd_Argv(2), "zone2")) {
631 updateZone = 2;
632 }
633 }
634
635 airequipSelectedSlot = ZONE_NONE;
636
637 /* select the slot corresponding to pos, and set airequipSelectedSlot to this slot */
638 switch (airequipID) {
639 case AC_ITEM_ELECTRONICS:
640 /* electronics selected */
641 for (i = 0; i < aircraft->maxElectronics; i++) {
642 if (aircraft->electronics[i].pos == pos) {
643 airequipSelectedSlot = i;
644 break;
645 }
646 }
647 if (i == aircraft->maxElectronics)
648 Com_Printf("this slot hasn't been found in aircraft electronics slots\n");
649 break;
650 case AC_ITEM_AMMO:
651 case AC_ITEM_WEAPON:
652 /* weapon selected */
653 for (i = 0; i < aircraft->maxWeapons; i++) {
654 if (aircraft->weapons[i].pos == pos) {
655 airequipSelectedSlot = i;
656 break;
657 }
658 }
659 if (i == aircraft->maxWeapons)
660 Com_Printf("this slot hasn't been found in aircraft weapon slots\n");
661 break;
662 default:
663 Com_Printf("AIM_AircraftEquipSlotSelect_f : only weapons and electronics have several slots\n");
664 break;
665 }
666
667 /* Update menu after changing slot */
668 AIM_AircraftEquipMenuUpdate();
669
670 /* update description with the selected slot */
671 if (updateZone > 0)
672 AIM_UpdateItemDescription(false, true);
673 else
674 AIM_UpdateItemDescription(true, false);
675 }
676
677 /**
678 * @brief Select the current zone you want to assign the item to.
679 */
AIM_AircraftEquipZoneSelect_f(void)680 static void AIM_AircraftEquipZoneSelect_f (void)
681 {
682 int zone;
683 aircraft_t* aircraft;
684 aircraftSlot_t* slot;
685 base_t* base = B_GetCurrentSelectedBase();
686
687 if (!base)
688 return;
689
690 if (cgi->Cmd_Argc() < 2) {
691 Com_Printf("Usage: %s <arg>\n", cgi->Cmd_Argv(0));
692 return;
693 }
694
695 zone = atoi(cgi->Cmd_Argv(1));
696
697 aircraft = base->aircraftCurrent;
698 assert(aircraft);
699 /* Select slot */
700 slot = AII_SelectAircraftSlot(aircraft, airequipID);
701
702 /* ammos are only available for weapons */
703 switch (airequipID) {
704 /* a weapon was selected - select ammo type corresponding to this weapon */
705 case AC_ITEM_WEAPON:
706 if (zone == ZONE_AMMO) {
707 if (slot->item)
708 airequipID = AC_ITEM_AMMO;
709 }
710 break;
711 /* an ammo was selected - select weapon type corresponding to this ammo */
712 case AC_ITEM_AMMO:
713 if (zone != ZONE_AMMO)
714 airequipID = AC_ITEM_WEAPON;
715 break;
716 default :
717 /* ZONE_AMMO is not available for electronics and shields */
718 if (zone == ZONE_AMMO)
719 return;
720 }
721 airequipSelectedZone = zone;
722
723 /* update menu */
724 AIM_AircraftEquipMenuUpdate();
725 /* Check that the selected zone is OK */
726 AIM_CheckAirequipSelectedZone(slot);
727
728 AIM_UpdateItemDescription(false, true);
729 }
730
731 /**
732 * @brief Add selected item to current zone.
733 * @note Called from airequip menu
734 * @sa aircraftItemType_t
735 */
AIM_AircraftEquipAddItem_f(void)736 static void AIM_AircraftEquipAddItem_f (void)
737 {
738 int zone;
739 aircraftSlot_t* slot;
740 aircraft_t* aircraft = nullptr;
741 base_t* base = B_GetCurrentSelectedBase();
742
743 zone = (airequipID == AC_ITEM_AMMO) ? 2 : 1;
744
745 /* proceed only if an item has been selected */
746 if (!aimSelectedTechnology)
747 return;
748
749 assert(base);
750 aircraft = base->aircraftCurrent;
751 assert(aircraft);
752 base = aircraft->homebase; /* we need to know where items will be removed */
753 slot = AII_SelectAircraftSlot(aircraft, airequipID);
754 if (slot == nullptr)
755 return;
756
757 /* the clicked button doesn't correspond to the selected zone */
758 if (zone != airequipSelectedZone)
759 return;
760
761 /* check if the zone exists */
762 if (zone >= ZONE_MAX)
763 return;
764
765 /* update the new item to slot */
766
767 switch (zone) {
768 case ZONE_MAIN:
769 if (!slot->nextItem) {
770 /* we add the weapon, shield, item if slot is free or the installation of current item just began */
771 if (!slot->item || (slot->item && slot->installationTime == slot->item->craftitem.installationTime)) {
772 AII_RemoveItemFromSlot(base, slot, false);
773 AII_AddItemToSlot(base, aimSelectedTechnology, slot, false); /* Aircraft stats are updated below */
774 AII_AutoAddAmmo(slot);
775 break;
776 } else if (slot->item == INVSH_GetItemByID(aimSelectedTechnology->provides)) {
777 /* the added item is the same than the one in current slot */
778 if (slot->installationTime == -slot->item->craftitem.installationTime) {
779 /* player changed his mind: he just want to re-add the item he just removed */
780 slot->installationTime = 0;
781 break;
782 } else if (!slot->installationTime) {
783 /* player try to add a weapon he already have: just skip */
784 return;
785 }
786 } else {
787 /* We start removing current item in slot, and the selected item will be installed afterwards */
788 slot->installationTime = -slot->item->craftitem.installationTime;
789 /* more below */
790 }
791 } else {
792 /* remove weapon and ammo of next item */
793 AII_RemoveNextItemFromSlot(base, slot, false);
794 /* more below */
795 }
796
797 /* we change the weapon, shield, item, or base defence that will be installed AFTER the removal
798 * of the one in the slot atm */
799 AII_AddItemToSlot(base, aimSelectedTechnology, slot, true);
800 AII_AutoAddAmmo(slot);
801 break;
802 case ZONE_AMMO:
803 /* we can change ammo only if the selected item is an ammo (for weapon or base defence system) */
804 if (airequipID >= AC_ITEM_AMMO) {
805 AII_AddAmmoToSlot(base, aimSelectedTechnology, slot);
806 }
807 break;
808 default:
809 /* Zone higher than ZONE_AMMO shouldn't exist */
810 return;
811 }
812
813 /* Update the values of aircraft stats (just in case an item has an installationTime of 0) */
814 AII_UpdateAircraftStats(aircraft);
815
816 AIM_AircraftEquipMenuUpdate();
817 }
818
819 /**
820 * @brief Delete an object from a zone.
821 */
AIM_AircraftEquipRemoveItem_f(void)822 static void AIM_AircraftEquipRemoveItem_f (void)
823 {
824 int zone;
825 aircraftSlot_t* slot;
826 aircraft_t* aircraft = nullptr;
827 base_t* base = B_GetCurrentSelectedBase();
828
829 zone = (airequipID == AC_ITEM_AMMO) ? 2 : 1;
830
831 assert(base);
832 aircraft = base->aircraftCurrent;
833 assert(aircraft);
834 slot = AII_SelectAircraftSlot(aircraft, airequipID);
835
836 /* no item in slot: nothing to remove */
837 if (!slot->item)
838 return;
839
840 /* update the new item to slot */
841
842 switch (zone) {
843 case ZONE_MAIN:
844 if (!slot->nextItem) {
845 /* we change the weapon, shield, item, or base defence that is already in the slot */
846 /* if the item has been installed since less than 1 hour, you don't need time to remove it */
847 if (slot->installationTime < slot->item->craftitem.installationTime) {
848 slot->installationTime = -slot->item->craftitem.installationTime;
849 AII_RemoveItemFromSlot(base, slot, true); /* we remove only ammo, not item */
850 } else {
851 AII_RemoveItemFromSlot(base, slot, false); /* we remove weapon and ammo */
852 }
853 /* aircraft stats are updated below */
854 } else {
855 /* we change the weapon, shield, item, or base defence that will be installed AFTER the removal
856 * of the one in the slot atm */
857 AII_RemoveNextItemFromSlot(base, slot, false); /* we remove weapon and ammo */
858 /* if you canceled next item for less than 1 hour, previous item is still functional */
859 if (slot->installationTime == -slot->item->craftitem.installationTime) {
860 slot->installationTime = 0;
861 }
862 }
863 break;
864 case ZONE_AMMO:
865 /* we can change ammo only if the selected item is an ammo (for weapon or base defence system) */
866 if (airequipID >= AC_ITEM_AMMO) {
867 if (slot->nextAmmo)
868 AII_RemoveNextItemFromSlot(base, slot, true);
869 else
870 AII_RemoveItemFromSlot(base, slot, true);
871 }
872 break;
873 default:
874 /* Zone higher than ZONE_AMMO shouldn't exist */
875 return;
876 }
877
878 /* Update the values of aircraft stats */
879 AII_UpdateAircraftStats(aircraft);
880
881 AIM_AircraftEquipMenuUpdate();
882 }
883
884 /**
885 * @brief Set AIM_selectedTechnology to the technology of current selected aircraft item.
886 * @sa AIM_AircraftEquipMenuUpdate_f
887 */
AIM_AircraftEquipMenuClick_f(void)888 static void AIM_AircraftEquipMenuClick_f (void)
889 {
890 int techIdx;
891
892 if (cgi->Cmd_Argc() < 2) {
893 Com_Printf("Usage: %s <num>\n", cgi->Cmd_Argv(0));
894 return;
895 }
896
897 /* Which tech? */
898 techIdx = atoi(cgi->Cmd_Argv(1));
899 aimSelectedTechnology = RS_GetTechByIDX(techIdx);
900 AIM_UpdateItemDescription(true, false);
901 }
902
903 /**
904 * @brief Update the GUI with a named itemtype
905 */
AIM_AircraftItemtypeByName_f(void)906 static void AIM_AircraftItemtypeByName_f (void)
907 {
908 aircraftItemType_t i;
909 const char* name;
910
911 if (cgi->Cmd_Argc() != 2) {
912 Com_Printf("Usage: %s <weapon|ammo|armour|item>\n", cgi->Cmd_Argv(0));
913 return;
914 }
915
916 name = cgi->Cmd_Argv(1);
917
918 if (Q_streq(name, "weapon"))
919 i = AC_ITEM_WEAPON;
920 else if (Q_streq(name, "ammo"))
921 i = AC_ITEM_AMMO;
922 else if (Q_streq(name, "armour"))
923 i = AC_ITEM_SHIELD;
924 else if (Q_streq(name, "item"))
925 i = AC_ITEM_ELECTRONICS;
926 else {
927 Com_Printf("AIM_AircraftItemtypeByName_f: Invalid itemtype!\n");
928 return;
929 }
930
931 airequipID = i;
932 cgi->Cmd_ExecuteString("airequip_updatemenu %d", airequipID);
933 }
934
AIM_InitCallbacks(void)935 void AIM_InitCallbacks (void)
936 {
937 cgi->Cmd_AddCommand("airequip_updatemenu", AIM_AircraftEquipMenuUpdate_f, "Init function for the aircraft equip menu");
938 cgi->Cmd_AddCommand("airequip_selectcategory", AIM_AircraftItemtypeByName_f, "Select an item category and update the GUI");
939 cgi->Cmd_AddCommand("airequip_list_click", AIM_AircraftEquipMenuClick_f, nullptr);
940 cgi->Cmd_AddCommand("airequip_slot_select", AIM_AircraftEquipSlotSelect_f, nullptr);
941 cgi->Cmd_AddCommand("airequip_add_item", AIM_AircraftEquipAddItem_f, "Add item to slot");
942 cgi->Cmd_AddCommand("airequip_remove_item", AIM_AircraftEquipRemoveItem_f, "Remove item from slot");
943 cgi->Cmd_AddCommand("airequip_zone_select", AIM_AircraftEquipZoneSelect_f, nullptr);
944 }
945
AIM_ShutdownCallbacks(void)946 void AIM_ShutdownCallbacks (void)
947 {
948 cgi->Cmd_RemoveCommand("airequip_updatemenu");
949 cgi->Cmd_RemoveCommand("airequip_selectcategory");
950 cgi->Cmd_RemoveCommand("airequip_list_click");
951 cgi->Cmd_RemoveCommand("airequip_slot_select");
952 cgi->Cmd_RemoveCommand("airequip_add_item");
953 cgi->Cmd_RemoveCommand("airequip_remove_item");
954 cgi->Cmd_RemoveCommand("airequip_zone_select");
955 }
956