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