UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_popup.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 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 "../../DateTime.h"
26#include "../../cl_shared.h"
27#include "cp_campaign.h"
28#include "cp_mapfightequip.h"
29#include "cp_geoscape.h"
30#include "cp_popup.h"
31#include "cp_missions.h"
32#include "cp_time.h"
34#include "../../ui/ui_dataids.h"
35
36/* popup_intercept display list of aircraft availables to move to a mission or a UFO */
37
39#define POPUP_INTERCEPT_MAX_AIRCRAFT 64
40
47
49
51static int popupNum;
52static linkedList_t* popupListData = nullptr;
53static uiNode_t* popupListNode = nullptr;
54
55static int INVALID_BASE = -1;
56
57/*========================================
58POPUP_HOMEBASE
59========================================*/
60
67bool CL_DisplayHomebasePopup (aircraft_t* aircraft, bool alwaysDisplay)
68{
69 int homebase;
70 int numAvailableBases = 0;
71 linkedList_t* popupListText = nullptr;
72 base_t* base;
73
74 assert(aircraft);
75
76 cgi->LIST_Delete(&popupListData);
77
78 popupNum = 0;
79 homebase = -1;
80
81 base = nullptr;
82 while ((base = B_GetNext(base)) != nullptr) {
83 char text[MAX_VAR];
84 char const* msg;
85
86 if (base == aircraft->homebase) {
87 msg = _("current homebase of aircraft");
89 homebase = popupNum;
90 } else {
91 msg = AIR_CheckMoveIntoNewHomebase(aircraft, base);
92 if (!msg) {
93 msg = _("base can hold aircraft");
94 LIST_Add(&popupListData, base->idx);
95 numAvailableBases++;
96 } else {
98 }
99 }
100
101 Com_sprintf(text, sizeof(text), "%s\t%s", base->name, msg);
102 cgi->LIST_AddString(&popupListText, text);
103 popupNum++;
104 }
105
106 if (alwaysDisplay || numAvailableBases > 0) {
108 popupListNode = cgi->UI_PopupList(_("Change homebase of aircraft"), _("Base\tStatus"), popupListText, "change_homebase <lineselected>;");
109 VectorSet(popupListNode->selectedColor, 0.0, 0.78, 0.0);
110 popupListNode->selectedColor[3] = 1.0;
111 cgi->UI_TextNodeSelectLine(popupListNode, homebase);
112 GEO_SelectAircraft(aircraft);
113 return true;
114 }
115
116 return false;
117}
118
123static void CL_PopupChangeHomebase_f (void)
124{
126
127 /* If popup is opened, that means an aircraft is selected */
128 if (!aircraft) {
129 cgi->Com_Printf("CL_PopupChangeHomebase_f: An aircraft must be selected\n");
130 return;
131 }
132
133 if (cgi->Cmd_Argc() < 2) {
134 cgi->Com_Printf("Usage: %s <popupIndex>\tpopupIndex=num in base list\n", cgi->Cmd_Argv(0));
135 return;
136 }
137
138 /* read and range check */
139 int selectedPopupIndex = atoi(cgi->Cmd_Argv(1));
140 cgi->Com_DPrintf(DEBUG_CLIENT, "CL_PopupHomebaseClick_f (popupNum %i, selectedPopupIndex %i)\n", popupNum, selectedPopupIndex);
141 if (selectedPopupIndex < 0 || selectedPopupIndex >= popupNum)
142 return;
143
144 /* Convert list index to base idx */
146 int baseIdx = INVALID_BASE;
147 for (int i = 0; data; data = data->next, i++) {
148 if (i == selectedPopupIndex) {
149 baseIdx = *(int*)data->data;
150 break;
151 }
152 }
153
154 base_t* base = B_GetFoundedBaseByIDX(baseIdx);
155 if (base == nullptr)
156 return;
157
158 if (!AIR_CheckMoveIntoNewHomebase(aircraft, base))
159 AIR_MoveAircraftIntoNewHomebase(aircraft, base);
160
161 cgi->UI_PopWindow(false);
162 CL_DisplayHomebasePopup(aircraft, true);
163}
164
165/*========================================
166POPUP_INTERCEPT
167========================================*/
168
169static int AIR_SortByDistance (linkedList_t* aircraftEntry1, linkedList_t* aircraftEntry2, const void* userData)
170{
171 const vec_t* pos = (const vec_t*)userData;
172 const aircraft_t* aircraft1 = (const aircraft_t*)aircraftEntry1->data;
173 const aircraft_t* aircraft2 = (const aircraft_t*)aircraftEntry2->data;
174
175 return GetDistanceOnGlobe(aircraft1->pos, pos) - GetDistanceOnGlobe(aircraft2->pos, pos);
176}
177
179#define AIR_ForeachSorted(var, sorter, userdata, sortedlist) LIST_ForeachSorted(ccs.aircraft, aircraft_t, var, sorter, userdata, sortedlist)
180
186{
187 linkedList_t* aircraftList = nullptr;
188 linkedList_t* aircraftListSorted;
189
190 if (!mission)
191 return;
192
193 popupIntercept.mission = mission;
194 popupIntercept.ufo = nullptr;
195
196 /* Create the list of aircraft, and write the text to display in popup */
197 popupIntercept.numAircraft = 0;
198
199 AIR_ForeachSorted(aircraft, AIR_SortByDistance, mission->pos, aircraftListSorted) {
200 const int teamSize = AIR_GetTeamSize(aircraft);
201
202 if (aircraft->status == AIR_CRASHED)
203 continue;
204 /* if aircraft is empty we can't send it on a ground mission */
205 if (teamSize > 0 && AIR_CanIntercept(aircraft)) {
206 char aircraftListText[256] = "";
207 const float distance = GetDistanceOnGlobe(aircraft->pos, mission->pos);
208 const char* statusName = AIR_AircraftStatusToName(aircraft);
209 const char* time = CP_SecondConvert((float)DateTime::SECONDS_PER_HOUR * distance / aircraft->stats[AIR_STATS_SPEED]);
210 Com_sprintf(aircraftListText, sizeof(aircraftListText), _("%s (%i/%i)\t%s\t%s\t%s"), aircraft->name,
211 teamSize, aircraft->maxTeamSize, statusName, aircraft->homebase->name, time);
212 cgi->LIST_AddString(&aircraftList, aircraftListText);
213 popupIntercept.aircraft[popupIntercept.numAircraft] = aircraft;
214 popupIntercept.numAircraft++;
216 break;
217 }
218 }
219 cgi->LIST_Delete(&aircraftListSorted);
220
221 if (popupIntercept.numAircraft)
222 cgi->UI_RegisterLinkedListText(TEXT_AIRCRAFT_LIST, aircraftList);
223 else
224 cgi->UI_RegisterText(TEXT_AIRCRAFT_LIST, _("No craft available, no pilot assigned, or no tactical teams assigned to available craft."));
225
226 /* Stop time */
228
229 /* Display the popup */
230 cgi->UI_PushWindow("popup_mission");
231}
232
233
239{
240 linkedList_t* aircraftList = nullptr;
241 linkedList_t* aircraftListSorted;
242 linkedList_t* baseList = nullptr;
243 base_t* base;
244
245 if (!ufo)
246 return;
247
248 popupIntercept.mission = nullptr;
249 popupIntercept.ufo = ufo;
250
251 /* Create the list of aircraft, and write the text to display in popup */
252 popupIntercept.numAircraft = 0;
253
254 AIR_ForeachSorted(aircraft, AIR_SortByDistance, ufo->pos, aircraftListSorted) {
255 if (AIR_CanIntercept(aircraft)) {
256 char aircraftListText[256] = "";
257 /* don't show aircraft with no weapons or no ammo, or crafts that
258 * can't even reach the target */
259 const char* enoughFuelMarker = "^B";
260
261 /* Does the aircraft has weapons and ammo ? */
262 if (AIRFIGHT_ChooseWeapon(aircraft->weapons, aircraft->maxWeapons, aircraft->pos, aircraft->pos) == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) {
263 cgi->Com_DPrintf(DEBUG_CLIENT, "CL_DisplayPopupIntercept: No useable weapon found in craft '%s' (%i)\n", aircraft->id, aircraft->maxWeapons);
264 continue;
265 }
266 /* now check the aircraft range */
267 if (!AIR_AircraftHasEnoughFuel(aircraft, ufo->pos)) {
268 cgi->Com_DPrintf(DEBUG_CLIENT, "CL_DisplayPopupIntercept: Target out of reach for craft '%s'\n", aircraft->id);
269 enoughFuelMarker = "";
270 }
271
272 Com_sprintf(aircraftListText, sizeof(aircraftListText), _("%s%s (%i/%i)\t%s\t%s"), enoughFuelMarker, aircraft->name,
273 AIR_GetTeamSize(aircraft), aircraft->maxTeamSize, AIR_AircraftStatusToName(aircraft), aircraft->homebase->name);
274 cgi->LIST_AddString(&aircraftList, aircraftListText);
275 popupIntercept.aircraft[popupIntercept.numAircraft] = aircraft;
276 popupIntercept.numAircraft++;
278 break;
279 }
280 }
281 cgi->LIST_Delete(&aircraftListSorted);
282
283 base = nullptr;
284 while ((base = B_GetNext(base)) != nullptr) {
285 /* Check if the base should be displayed in base list
286 * don't check range because maybe UFO will get closer */
287 if (AII_BaseCanShoot(base))
288 cgi->LIST_AddString(&baseList, va("^B%s", base->name));
289 } /* bases */
290
291 if (popupIntercept.numAircraft)
292 cgi->UI_RegisterLinkedListText(TEXT_AIRCRAFT_LIST, aircraftList);
293 else
294 cgi->UI_RegisterText(TEXT_AIRCRAFT_LIST, _("No craft available, no pilot assigned, or no weapon or ammo equipped."));
295
296 INS_Foreach(installation) {
297 /* Check if the installation should be displayed in base list
298 * don't check range because maybe UFO will get closer */
299 if (AII_InstallationCanShoot(installation))
300 cgi->LIST_AddString(&baseList, va("^B%s", installation->name));
301 }
302
303 if (baseList)
304 cgi->UI_RegisterLinkedListText(TEXT_BASE_LIST, baseList);
305 else
306 cgi->UI_RegisterText(TEXT_BASE_LIST, _("No defence system operational or no weapon or ammo equipped."));
307
308 /* Stop time */
310
311 /* Display the popup */
312 cgi->UI_PushWindow("popup_intercept");
313}
314
320{
321 int num;
322
323 if (cgi->Cmd_Argc() < 2)
324 return nullptr;
325
326 /* Get the selected aircraft */
327 num = atoi(cgi->Cmd_Argv(1));
328 if (num < 0 || num >= popupIntercept.numAircraft)
329 return nullptr;
330
331 cgi->UI_PopWindow(false);
332 if (!popupIntercept.aircraft[num])
333 return nullptr;
334 return popupIntercept.aircraft[num];
335}
336
341static void CL_PopupInterceptClick_f (void)
342{
343 aircraft_t* aircraft;
344 base_t* base;
345
346 /* Get the selected aircraft */
347 aircraft = CL_PopupInterceptGetAircraft();
348 if (aircraft == nullptr)
349 return;
350
351 /* Aircraft can start if only Command Centre in base is operational. */
352 base = aircraft->homebase;
353 if (!B_GetBuildingStatus(base, B_COMMAND)) {
355 CP_Popup(_("Notice"), _("No Command Centre operational in homebase\nof this aircraft.\n\nAircraft cannot start.\n"));
356 return;
357 }
358
359 /* Set action to aircraft */
360 if (popupIntercept.mission)
361 AIR_SendAircraftToMission(aircraft, popupIntercept.mission); /* Aircraft move to mission */
362 else if (popupIntercept.ufo)
363 AIR_SendAircraftPursuingUFO(aircraft, popupIntercept.ufo); /* Aircraft purchase ufo */
364}
365
371{
372 aircraft_t* aircraft;
373
374 /* Get the selected aircraft */
375 aircraft = CL_PopupInterceptGetAircraft();
376 if (aircraft == nullptr)
377 return;
378
379 /* Display aircraft menu */
380 AIR_AircraftSelect(aircraft);
382 B_SelectBase(aircraft->homebase);
383 cgi->UI_PushWindow("aircraft");
384}
385
391{
392 if (cgi->Cmd_Argc() < 2) {
393 cgi->Com_Printf("Usage: %s <num>\tnum=num in base list\n", cgi->Cmd_Argv(0));
394 return;
395 }
396
397 /* If popup is opened, that means that ufo is selected on geoscape */
398 if (GEO_GetSelectedUFO() == nullptr)
399 return;
400
401 int num = atoi(cgi->Cmd_Argv(1));
402
403 base_t* base = nullptr;
404 bool atLeastOneBase = false;
405 while ((base = B_GetNext(base)) != nullptr) {
406 /* Check if the base should be displayed in base list */
407 if (AII_BaseCanShoot(base)) {
408 num--;
409 atLeastOneBase = true;
410 if (num < 0)
411 break;
412 }
413 }
414
415 installation_t* installation = nullptr;
416 if (num >= 0) { /* don't try to find an installation if we already found the right base */
417 INS_Foreach(inst) {
418 /* Check if the installation should be displayed in base list */
419 if (AII_InstallationCanShoot(inst)) {
420 num--;
421 atLeastOneBase = true;
422 if (num < 0) {
423 installation = inst;
424 break;
425 }
426 }
427 }
428 }
429
430 if (!atLeastOneBase && !num) {
431 /* no base in list: no error message
432 * note that num should always be 0 if we enter this loop, unless this function is called from console
433 * so 2nd part of the test should be useless in most case */
434 return;
435 } else if (num >= 0) {
436 cgi->Com_Printf("CL_PopupInterceptBaseClick_f: Number given in argument (%i) is bigger than number of base in list.\n", num);
437 return;
438 }
439
440 assert(base || installation);
441 int i;
442 if (installation) {
443 for (i = 0; i < installation->installationTemplate->maxBatteries; i++)
444 installation->batteries[i].target = GEO_GetSelectedUFO();
445 } else {
446 for (i = 0; i < base->numBatteries; i++)
448 for (i = 0; i < base->numLasers; i++)
450 }
451
452 cgi->UI_PopWindow(false);
453}
454
458void CL_PopupInit (void)
459{
460 /* popup_intercept commands */
461 cgi->Cmd_AddCommand("ships_click", CL_PopupInterceptClick_f, nullptr);
462 cgi->Cmd_AddCommand("ships_rclick", CL_PopupInterceptRClick_f, nullptr);
463 cgi->Cmd_AddCommand("bases_click", CL_PopupInterceptBaseClick_f, nullptr);
464
465 /* popup_homebase commands */
466 cgi->Cmd_AddCommand("change_homebase", CL_PopupChangeHomebase_f, nullptr);
467
469}
470
474void CP_Popup (const char* title, const char* text, ...)
475{
476 static char msg[1024];
477 va_list argptr;
478
479 va_start(argptr, text);
480 Q_vsnprintf(msg, sizeof(msg), text, argptr);
481 va_end(argptr);
482
483 cgi->UI_Popup(title, msg);
484}
DateTime class definition.
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
static linkedList_t * popupListData
Definition cl_hud.cpp:258
static uiNode_t * popupListNode
Definition cl_hud.cpp:259
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
static const short SECONDS_PER_HOUR
Definition DateTime.h:42
bool AIR_CanIntercept(const aircraft_t *aircraft)
const char * AIR_AircraftStatusToName(const aircraft_t *aircraft)
Translates the aircraft status id to a translatable string.
bool AIR_AircraftHasEnoughFuel(const aircraft_t *aircraft, const vec2_t destination)
check if aircraft has enough fuel to go to destination, and then come back home
bool AIR_SendAircraftToMission(aircraft_t *aircraft, mission_t *mission)
Sends the specified aircraft to specified mission.
void AIR_MoveAircraftIntoNewHomebase(aircraft_t *aircraft, base_t *base)
Moves a given aircraft to a new base (also the employees and inventory).
bool AIR_SendAircraftPursuingUFO(aircraft_t *aircraft, aircraft_t *ufo)
Make the specified aircraft purchasing a UFO.
int AIR_GetTeamSize(const aircraft_t *aircraft)
Counts the number of soldiers in given aircraft.
const char * AIR_CheckMoveIntoNewHomebase(const aircraft_t *aircraft, const base_t *base)
Checks if destination base can store an aircraft and its team.
@ AIR_CRASHED
void AIR_AircraftSelect(aircraft_t *aircraft)
Sets aircraftCurrent and updates related cvars and menutexts.
Header file for menu related console command callbacks.
int AIRFIGHT_ChooseWeapon(const aircraftSlot_t *slot, int maxSlot, const vec2_t pos, const vec2_t targetPos)
Choose the weapon an attacking aircraft will use to fire on a target.
#define AIRFIGHT_WEAPON_CAN_NEVER_SHOOT
Definition cp_airfight.h:38
base_t * B_GetFoundedBaseByIDX(int baseIdx)
Array bound check for the base index.
Definition cp_base.cpp:326
void B_SelectBase(const base_t *base)
Select and opens a base.
Definition cp_base.cpp:1592
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition cp_base.cpp:286
bool B_GetBuildingStatus(const base_t *const base, const buildingType_t buildingType)
Get the status associated to a building.
Definition cp_base.cpp:478
@ B_COMMAND
Definition cp_building.h:62
Header file for single player campaign control.
const cgame_import_t * cgi
void GEO_SelectAircraft(aircraft_t *aircraft)
Select the specified aircraft on the geoscape.
void GEO_ResetAction(void)
No more special action on the geoscape.
Header for Geoscape management.
#define GEO_GetSelectedUFO()
Definition cp_geoscape.h:58
#define GEO_GetSelectedAircraft()
Definition cp_geoscape.h:56
#define INS_Foreach(var)
int AII_BaseCanShoot(const base_t *base)
Check if the base has weapon and ammo.
bool AII_InstallationCanShoot(const installation_t *installation)
Check if the installation has a weapon and ammo.
Header for slot management related stuff.
Campaign missions headers.
#define POPUP_INTERCEPT_MAX_AIRCRAFT
Definition cp_popup.cpp:39
static void CL_PopupInterceptClick_f(void)
User select an item in the popup_aircraft Make the aircraft attack the corresponding mission or UFO.
Definition cp_popup.cpp:341
static void CL_PopupInterceptBaseClick_f(void)
User select a base in the popup_aircraft Make the base attack the corresponding UFO.
Definition cp_popup.cpp:390
static void CL_PopupChangeHomebase_f(void)
User select a base in the popup_homebase change homebase to selected base.
Definition cp_popup.cpp:123
bool CL_DisplayHomebasePopup(aircraft_t *aircraft, bool alwaysDisplay)
Display the popup_homebase.
Definition cp_popup.cpp:67
static popup_intercept_t popupIntercept
Definition cp_popup.cpp:48
void CP_Popup(const char *title, const char *text,...)
Wrapper around UI_Popup.
Definition cp_popup.cpp:474
static int popupNum
Definition cp_popup.cpp:51
static aircraft_t * CL_PopupInterceptGetAircraft(void)
return the selected aircraft in popup_intercept Close the popup if required
Definition cp_popup.cpp:319
static void CL_PopupInterceptRClick_f(void)
User select an item in the popup_aircraft with right click Opens up the aircraft menu.
Definition cp_popup.cpp:370
void CL_DisplayPopupInterceptUFO(aircraft_t *ufo)
Display the popup_intercept.
Definition cp_popup.cpp:238
static int INVALID_BASE
Definition cp_popup.cpp:55
void CL_DisplayPopupInterceptMission(mission_t *mission)
Display the popup_mission.
Definition cp_popup.cpp:185
#define AIR_ForeachSorted(var, sorter, userdata, sortedlist)
Definition cp_popup.cpp:179
static int AIR_SortByDistance(linkedList_t *aircraftEntry1, linkedList_t *aircraftEntry2, const void *userData)
Definition cp_popup.cpp:169
void CL_PopupInit(void)
Initialise popups.
Definition cp_popup.cpp:458
const char * CP_SecondConvert(int second)
Converts a number of second into a char to display.
Definition cp_time.cpp:57
void CP_GameTimeStop(void)
Stop game time speed.
Definition cp_time.cpp:126
Campaign geoscape time header.
#define DEBUG_CLIENT
Definition defines.h:59
@ AIR_STATS_SPEED
Definition inv_shared.h:226
double GetDistanceOnGlobe(const vec2_t pos1, const vec2_t pos2)
Calculate distance on the geoscape.
Definition mathlib.cpp:171
QGL_EXTERN GLsizei const GLvoid * data
Definition r_gl.h:89
QGL_EXTERN GLint i
Definition r_gl.h:113
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
Safe (null terminating) vsnprintf implementation.
Definition shared.cpp:535
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
An aircraft with all it's data.
struct base_s * homebase
A base with all it's data.
Definition cp_base.h:84
int numLasers
Definition cp_base.h:120
baseWeapon_t batteries[MAX_BASE_SLOT]
Definition cp_base.h:116
baseWeapon_t lasers[MAX_BASE_SLOT]
Definition cp_base.h:119
int idx
Definition cp_base.h:85
char name[MAX_VAR]
Definition cp_base.h:86
int numBatteries
Definition cp_base.h:117
aircraft_t * target
Definition cp_base.h:79
A installation with all it's data.
const installationTemplate_t * installationTemplate
baseWeapon_t batteries[MAX_INSTALLATION_BATTERIES]
void * data
Definition list.h:31
mission definition
Definition cp_missions.h:86
vec2_t pos
aircraft_t * ufo
Definition cp_popup.cpp:45
mission_t * mission
Definition cp_popup.cpp:44
aircraft_t * aircraft[POPUP_INTERCEPT_MAX_AIRCRAFT]
Definition cp_popup.cpp:43
Atomic structure used to define most of the UI.
Definition ui_nodes.h:80
float vec_t
Definition ufotypes.h:37
@ TEXT_AIRCRAFT_LIST
Definition ui_dataids.h:40
@ TEXT_BASE_LIST
Definition ui_dataids.h:47
#define VectorSet(v, x, y, z)
Definition vector.h:59