UFO: Alien Invasion
cp_popup.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2022 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 "../../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"
33 #include "cp_aircraft_callbacks.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 
41 typedef struct popup_intercept_s {
47 
51 static int popupNum;
52 static linkedList_t* popupListData = nullptr;
53 static uiNode_t* popupListNode = nullptr;
55 static int INVALID_BASE = -1;
56 
57 /*========================================
58 POPUP_HOMEBASE
59 ========================================*/
60 
67 bool 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) {
107  CP_GameTimeStop();
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 
123 static void CL_PopupChangeHomebase_f (void)
124 {
125  aircraft_t* aircraft = GEO_GetSelectedAircraft();
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 /*========================================
166 POPUP_INTERCEPT
167 ========================================*/
168 
169 static 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 */
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);
216  break;
217  }
218  }
219  cgi->LIST_Delete(&aircraftListSorted);
220 
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 */
227  CP_GameTimeStop();
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 */
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);
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 
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 */
309  CP_GameTimeStop();
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 
341 static 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 */
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 
370 static void CL_PopupInterceptRClick_f (void)
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);
381  GEO_ResetAction();
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++)
447  base->batteries[i].target = GEO_GetSelectedUFO();
448  for (i = 0; i < base->numLasers; i++)
449  base->lasers[i].target = GEO_GetSelectedUFO();
450  }
451 
452  cgi->UI_PopWindow(false);
453 }
454 
458 void 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 
474 void 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 }
struct base_s * homebase
Definition: cp_aircraft.h:150
int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
Safe (null terminating) vsnprintf implementation.
Definition: shared.cpp:535
#define AIRFIGHT_WEAPON_CAN_NEVER_SHOOT
Definition: cp_airfight.h:38
#define VectorSet(v, x, y, z)
Definition: vector.h:59
uiNode_t *IMPORT * UI_PopupList(const char *title, const char *headline, linkedList_t *entries, const char *clickAction)
mission definition
Definition: cp_missions.h:86
bool B_GetBuildingStatus(const base_t *const base, const buildingType_t buildingType)
Get the status associated to a building.
Definition: cp_base.cpp:478
bool AIR_SendAircraftPursuingUFO(aircraft_t *aircraft, aircraft_t *ufo)
Make the specified aircraft purchasing a UFO.
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
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don&#39;t need to have varargs versions of all text functi...
Definition: shared.cpp:410
A base with all it&#39;s data.
Definition: cp_base.h:84
const char * AIR_AircraftStatusToName(const aircraft_t *aircraft)
Translates the aircraft status id to a translatable string.
#define _(String)
Definition: cl_shared.h:44
void * data
Definition: list.h:31
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
static aircraft_t * CL_PopupInterceptGetAircraft(void)
return the selected aircraft in popup_intercept Close the popup if required
Definition: cp_popup.cpp:319
float vec_t
Definition: ufotypes.h:37
bool AII_InstallationCanShoot(const installation_t *installation)
Check if the installation has a weapon and ammo.
char name[MAX_VAR]
Definition: cp_base.h:86
void AIR_MoveAircraftIntoNewHomebase(aircraft_t *aircraft, base_t *base)
Moves a given aircraft to a new base (also the employees and inventory)
QGL_EXTERN GLsizei const GLvoid * data
Definition: r_gl.h:89
bool CL_DisplayHomebasePopup(aircraft_t *aircraft, bool alwaysDisplay)
Display the popup_homebase.
Definition: cp_popup.cpp:67
#define GEO_GetSelectedAircraft()
Definition: cp_geoscape.h:56
const char *IMPORT * Cmd_Argv(int n)
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
A installation with all it&#39;s data.
void CP_GameTimeStop(void)
Stop game time speed.
Definition: cp_time.cpp:126
base_t * B_GetFoundedBaseByIDX(int baseIdx)
Array bound check for the base index.
Definition: cp_base.cpp:326
void GEO_ResetAction(void)
No more special action on the geoscape.
Header file for menu related console command callbacks.
#define INS_Foreach(var)
int idx
Definition: cp_base.h:85
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.
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
#define DEBUG_CLIENT
Definition: defines.h:59
static popup_intercept_t popupIntercept
Definition: cp_popup.cpp:48
#define OBJZERO(obj)
Definition: shared.h:178
void CL_DisplayPopupInterceptUFO(aircraft_t *ufo)
Display the popup_intercept.
Definition: cp_popup.cpp:238
void CL_DisplayPopupInterceptMission(mission_t *mission)
Display the popup_mission.
Definition: cp_popup.cpp:185
#define MAX_VAR
Definition: shared.h:36
static int AIR_SortByDistance(linkedList_t *aircraftEntry1, linkedList_t *aircraftEntry2, const void *userData)
Definition: cp_popup.cpp:169
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
void CP_Popup(const char *title, const char *text,...)
Wrapper around UI_Popup.
Definition: cp_popup.cpp:474
Campaign missions headers.
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition: cp_base.cpp:286
const cgame_import_t * cgi
aircraft_t * target
Definition: cp_base.h:79
vec3_t pos
Definition: cp_aircraft.h:132
Campaign geoscape time header.
vec2_t pos
Definition: cp_missions.h:105
Header for Geoscape management.
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
baseWeapon_t batteries[MAX_INSTALLATION_BATTERIES]
static void CL_PopupChangeHomebase_f(void)
User select a base in the popup_homebase change homebase to selected base.
Definition: cp_popup.cpp:123
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
An aircraft with all it&#39;s data.
Definition: cp_aircraft.h:115
bool AIR_CanIntercept(const aircraft_t *aircraft)
vec4_t selectedColor
Definition: ui_nodes.h:128
mission_t * mission
Definition: cp_popup.cpp:44
#define POPUP_INTERCEPT_MAX_AIRCRAFT
Definition: cp_popup.cpp:39
static const short SECONDS_PER_HOUR
Definition: DateTime.h:42
QGL_EXTERN GLint i
Definition: r_gl.h:113
static int INVALID_BASE
Definition: cp_popup.cpp:55
int AIR_GetTeamSize(const aircraft_t *aircraft)
Counts the number of soldiers in given aircraft.
aircraft_t * ufo
Definition: cp_popup.cpp:45
Header for slot management related stuff.
bool AIR_SendAircraftToMission(aircraft_t *aircraft, mission_t *mission)
Sends the specified aircraft to specified mission.
Header file for single player campaign control.
#define GEO_GetSelectedUFO()
Definition: cp_geoscape.h:58
void CL_PopupInit(void)
Initialise popups.
Definition: cp_popup.cpp:458
int AII_BaseCanShoot(const base_t *base)
Check if the base has weapon and ammo.
static uiNode_t * popupListNode
Definition: cp_popup.cpp:53
static int popupNum
Definition: cp_popup.cpp:51
static linkedList_t * popupListData
Definition: cp_popup.cpp:52
aircraft_t * aircraft[POPUP_INTERCEPT_MAX_AIRCRAFT]
Definition: cp_popup.cpp:43
const char * AIR_CheckMoveIntoNewHomebase(const aircraft_t *aircraft, const base_t *base)
Checks if destination base can store an aircraft and its team.
const installationTemplate_t * installationTemplate
void B_SelectBase(const base_t *base)
Select and opens a base.
Definition: cp_base.cpp:1592
void AIR_AircraftSelect(aircraft_t *aircraft)
Sets aircraftCurrent and updates related cvars and menutexts.
const char * CP_SecondConvert(int second)
Converts a number of second into a char to display.
Definition: cp_time.cpp:57
#define AIR_ForeachSorted(var, sorter, userdata, sortedlist)
Definition: cp_popup.cpp:179
void GEO_SelectAircraft(aircraft_t *aircraft)
Select the specified aircraft on the geoscape.
double GetDistanceOnGlobe(const vec2_t pos1, const vec2_t pos2)
Calculate distance on the geoscape.
Definition: mathlib.cpp:171