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