UFO: Alien Invasion
cp_transfer_callbacks.cpp
Go to the documentation of this file.
1 
5 /*
6 Copyright (C) 2002-2022 UFO: Alien Invasion.
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 See the GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23 
24 #include "cp_transfer_callbacks.h"
25 #include "../../DateTime.h"
26 #include "../../cl_shared.h"
27 #include "cp_campaign.h"
28 #include "cp_capacity.h"
29 #include "cp_transfer.h"
30 #include "cp_popup.h"
31 #include "cp_time.h"
32 #include "aliencargo.h"
33 #include "aliencontainment.h"
34 #include "itemcargo.h"
35 
39 typedef enum {
45 
48 
52 static char const* const transferTypeIDs[] = {
53  "item",
54  "employee",
55  "alien",
56  "aircraft"
57 };
59 
61 static transfer_t tr;
64 
68 static void TR_ClearTempCargo (void)
69 {
70  tr.antimatter = 0;
71  if (tr.itemCargo != nullptr) {
72  delete tr.itemCargo;
73  tr.itemCargo = nullptr;
74  }
75  if (tr.alienCargo != nullptr) {
76  delete tr.alienCargo;
77  tr.alienCargo = nullptr;
78  }
79  for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
80  const employeeType_t emplType = (employeeType_t)i;
81  cgi->LIST_Delete(&tr.employees[emplType]);
82  }
83  cgi->LIST_Delete(&tr.aircraft);
84 }
85 
89 static void TR_TransferStart_f (void)
90 {
91  char message[1024];
93 
95  cgi->Com_Printf("TR_TransferStart_f: currentTransferType is wrong!\n");
96  return;
97  }
98 
99  if (TR_TransferStart(base, tr) == nullptr)
100  return;
102 
103  Com_sprintf(message, sizeof(message), _("Transport mission started, cargo is being transported to %s"), tr.destBase->name);
104  MSO_CheckAddNewMessage(NT_TRANSFER_STARTED, _("Transport mission"), message, MSG_TRANSFERFINISHED);
105  cgi->UI_PopWindow(false);
106 }
107 
113 static transferType_t TR_GetTransferType (const char* id)
114 {
115  for (int i = 0; i < TRANS_TYPE_MAX; i++) {
116  if (Q_streq(transferTypeIDs[i], id))
117  return (transferType_t)i;
118  }
119  return TRANS_TYPE_INVALID;
120 }
121 
125 static void TR_CargoList (void)
126 {
127  /* reset for every new call */
128  cgi->UI_ExecuteConfunc("ui_cargolist_clear");
129 
130  /* Show Antimatter */
131  if (tr.antimatter > 0) {
133  cgi->UI_ExecuteConfunc("ui_cargolist_add \"%s\" \"%s\" %d", od->id, _(od->name), tr.antimatter);
134  }
135 
136  /* Show items */
137  if (tr.itemCargo != nullptr) {
139  LIST_Foreach(cargo, itemCargo_t, item) {
140  if (item->amount > 0)
141  cgi->UI_ExecuteConfunc("ui_cargolist_add \"%s\" \"%s\" %d", item->objDef->id, _(item->objDef->name), item->amount);
142  }
143  cgi->LIST_Delete(&cargo);
144  }
145 
146  /* Show employees */
147  for (int i = 0; i < MAX_EMPL; i++) {
148  const employeeType_t emplType = (employeeType_t)i;
149  switch (emplType) {
150  case EMPL_SOLDIER:
151  case EMPL_PILOT:
152  LIST_Foreach(tr.employees[emplType], Employee, employee) {
153  if (emplType == EMPL_SOLDIER) {
154  const rank_t* rank = CL_GetRankByIdx(employee->chr.score.rank);
155  cgi->UI_ExecuteConfunc("ui_cargolist_add \"ucn_%d\" \"%s %s %s\" %d", employee->chr.ucn,
156  E_GetEmployeeString((employeeType_t)emplType, 1), _(rank->shortname), employee->chr.name, 1);
157  } else {
158  cgi->UI_ExecuteConfunc("ui_cargolist_add \"ucn_%d\" \"%s %s\" %d", employee->chr.ucn,
159  E_GetEmployeeString((employeeType_t)emplType, 1), employee->chr.name, 1);
160  }
161  }
162  break;
163  case EMPL_ROBOT:
165  break;
166  case EMPL_SCIENTIST:
167  case EMPL_WORKER: {
168  int emplCount = cgi->LIST_Count(tr.employees[emplType]);
169  if (emplCount <= 0)
170  break;
171  cgi->UI_ExecuteConfunc("ui_cargolist_add \"%s\" \"%s\" %d", (emplType == EMPL_SCIENTIST) ? "scientist" : "worker",
172  E_GetEmployeeString((employeeType_t)emplType, emplCount), emplCount);
173  break;
174  }
175  default:
176  cgi->Com_Error(ERR_DROP, "TR_CargoList: Invalid employeetype in cargo");
177  }
178  }
179 
180  /* Show aliens */
181  if (tr.alienCargo != nullptr) {
184  if (item->dead > 0)
185  cgi->UI_ExecuteConfunc("ui_cargolist_add \"dead_%s\" \"%s\" %d", item->teamDef->id, va(_("Corpse of %s"), _(item->teamDef->name)), item->dead);
186  if (item->alive > 0)
187  cgi->UI_ExecuteConfunc("ui_cargolist_add \"alive_%s\" \"%s\" %d", item->teamDef->id, va(_("Alive %s"), _(item->teamDef->name)), item->alive);
188  }
189  cgi->LIST_Delete(&cargo);
190  }
191 
192  /* Show all aircraft */
193  LIST_Foreach(tr.aircraft, aircraft_t, aircraft) {
194  cgi->UI_ExecuteConfunc("ui_cargolist_add \"aircraft_%d\" \"%s\" %d", aircraft->idx, va(_("Aircraft %s"), aircraft->name), 1);
195  }
196 }
197 
203 static bool TR_AircraftListSelect (const aircraft_t* aircraft)
204 {
205  if (!AIR_IsAircraftInBase(aircraft)) /* Aircraft is not in base. */
206  return false;
207  if (cgi->LIST_GetPointer(tr.aircraft, aircraft)) /* Already on transfer list. */
208  return false;
209 
210  return true;
211 }
212 
218 static void TR_FillItems (const base_t* srcBase, const base_t* destBase)
219 {
220  const objDef_t* od;
221 
223  if (od) {
224  const int antiMatterInCargo = tr.antimatter;
225  const int antiMatterInSrcBase = B_AntimatterInBase(srcBase);
226  const int antiMatterInDstBase = B_AntimatterInBase(destBase);
227 
228  if (antiMatterInCargo || antiMatterInSrcBase) {
229  cgi->UI_ExecuteConfunc("ui_translist_add \"%s\" \"%s\" %d %d %d %d %d", od->id, _(od->name),
230  antiMatterInSrcBase - antiMatterInCargo, antiMatterInDstBase, 0, antiMatterInCargo, antiMatterInSrcBase);
231  }
232  }
233  for (int i = 0; i < cgi->csi->numODs; i++) {
234  od = INVSH_GetItemByIDX(i);
235  assert(od);
237  continue;
238  const int itemCargoAmount = tr.itemCargo ? tr.itemCargo->getAmount(od) : 0;
239  const int itemInSrcBase = B_ItemInBase(od, srcBase);
240  const int itemInDstBase = B_ItemInBase(od, destBase);
241  if (itemCargoAmount || itemInSrcBase > 0) {
242  cgi->UI_ExecuteConfunc("ui_translist_add \"%s\" \"%s\" %d %d %d %d %d", od->id, _(od->name),
243  itemInSrcBase - itemCargoAmount, itemInDstBase, 0, itemCargoAmount, itemInSrcBase);
244  }
245  }
246 }
247 
253 static void TR_FillEmployees (const base_t* srcBase, const base_t* destBase)
254 {
255  for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
256  const employeeType_t emplType = (employeeType_t)i;
257  switch (emplType) {
258  case EMPL_SOLDIER:
259  case EMPL_PILOT: {
260  E_Foreach(emplType, employee) {
261  char str[128];
262 
263  if (!employee->isHiredInBase(srcBase))
264  continue;
265 
266  /* Skip if already on transfer list. */
267  if (cgi->LIST_GetPointer(tr.employees[emplType], (void*) employee))
268  continue;
269 
270  if (emplType == EMPL_SOLDIER) {
271  const rank_t* rank = CL_GetRankByIdx(employee->chr.score.rank);
272  Com_sprintf(str, sizeof(str), "%s %s %s", E_GetEmployeeString(emplType, 1), _(rank->shortname), employee->chr.name);
273  } else {
274  Com_sprintf(str, sizeof(str), "%s %s", E_GetEmployeeString(emplType, 1), employee->chr.name);
275  }
276 
277  cgi->UI_ExecuteConfunc("ui_translist_add \"ucn_%d\" \"%s\" %d %d %d %d %d", employee->chr.ucn,
278  str, -1, -1, -1, -1, -1);
279  }
280  break;
281  }
282  case EMPL_ROBOT:
284  break;
285  case EMPL_SCIENTIST:
286  case EMPL_WORKER: {
287  const int hiredSrc = E_CountHired(srcBase, emplType);
288  const int hiredDst = E_CountHired(destBase, emplType);
289  const int trCount = cgi->LIST_Count(tr.employees[emplType]);
290 
291  if (hiredSrc <= 0)
292  break;
293 
294  cgi->UI_ExecuteConfunc("ui_translist_add \"%s\" \"%s\" %d %d %d %d %d",
295  (emplType == EMPL_SCIENTIST) ? "scientist" : "worker", E_GetEmployeeString(emplType, hiredSrc),
296  hiredSrc - trCount, hiredDst, 0, trCount, hiredSrc);
297  break;
298  }
299  default:
300  cgi->Com_Error(ERR_DROP, "TR_CargoList: Invalid employeetype in cargo");
301  }
302  }
303 }
304 
310 static void TR_FillAliens (const base_t* srcBase, const base_t* destBase)
311 {
312  if (!srcBase->alienContainment)
313  return;
314 
315  linkedList_t* list = srcBase->alienContainment->list();
316  LIST_Foreach(list, alienCargo_t, item) {
317  const int srcDead = item->dead;
318  const int srcAlive = item->alive;
319  const int dstDead = (destBase->alienContainment) ? destBase->alienContainment->getDead(item->teamDef) : 0;
320  const int dstAlive = (destBase->alienContainment) ? destBase->alienContainment->getAlive(item->teamDef) : 0;
321  const int transferDead = (tr.alienCargo) ? tr.alienCargo->getDead(item->teamDef) : 0;
322  const int transferAlive = (tr.alienCargo) ? tr.alienCargo->getAlive(item->teamDef) : 0;
323 
324  if (srcDead > 0 || transferDead > 0) {
325  char str[128];
326  Com_sprintf(str, sizeof(str), _("Corpse of %s"), _(item->teamDef->name));
327  cgi->UI_ExecuteConfunc("ui_translist_add \"dead_%s\" \"%s\" %d %d %d %d %d",
328  item->teamDef->id, str, srcDead - transferDead, dstDead, 0, transferDead, srcDead);
329  }
330  if (srcAlive > 0 || transferAlive > 0) {
331  char str[128];
332  Com_sprintf(str, sizeof(str), _("Alive %s"), _(item->teamDef->name));
333  cgi->UI_ExecuteConfunc("ui_translist_add \"alive_%s\" \"%s\" %d %d %d %d %d",
334  item->teamDef->id, str, srcAlive - transferAlive, dstAlive, 0, transferAlive, srcAlive);
335  }
336  }
337  cgi->LIST_Delete(&list);
338 }
339 
345 static void TR_FillAircraft (const base_t* srcBase, const base_t* destBase)
346 {
347  AIR_ForeachFromBase(aircraft, srcBase) {
348  /* Aircraft is not in base. */
349  if (!AIR_IsAircraftInBase(aircraft))
350  continue;
351  /* Already on transfer list. */
352  if (cgi->LIST_GetPointer(tr.aircraft, aircraft))
353  continue;
354 
355  cgi->UI_ExecuteConfunc("ui_translist_add \"aircraft_%d\" \"%s\" %d %d %d %d %d",
356  aircraft->idx, aircraft->name, -1, -1, -1, -1, -1);
357  }
358 }
359 
368 static void TR_Fill (const base_t* srcBase, const base_t* destBase, transferType_t transferType)
369 {
370  if (srcBase == nullptr || destBase == nullptr)
371  return;
372 
373  currentTransferType = transferType;
374  /* reset for every new call */
375  cgi->UI_ExecuteConfunc("ui_translist_clear");
376  switch (transferType) {
377  case TRANS_TYPE_ITEM:
378  TR_FillItems(srcBase, destBase);
379  break;
380  case TRANS_TYPE_EMPLOYEE:
381  TR_FillEmployees(srcBase, destBase);
382  break;
383  case TRANS_TYPE_ALIEN:
384  TR_FillAliens(srcBase, destBase);
385  break;
386  case TRANS_TYPE_AIRCRAFT:
387  TR_FillAircraft(srcBase, destBase);
388  break;
389  default:
390  cgi->Com_Error(ERR_DROP, "invalid transfertype given: %i", transferType);
391  }
392  /* Update cargo list. */
393  TR_CargoList();
394 }
395 
399 static void TR_Fill_f (void)
400 {
402  const base_t* base = B_GetCurrentSelectedBase();
403 
404  if (!tr.destBase || !base)
405  return;
406  if (cgi->Cmd_Argc() < 2)
408  else
410  if (type == TRANS_TYPE_INVALID)
411  return;
412  TR_Fill(base, tr.destBase, type);
413 }
414 
418 static void TR_Add_f (void)
419 {
421 
422  if (cgi->Cmd_Argc() < 3) {
423  cgi->Com_Printf("Usage: %s <itemid> <amount>", cgi->Cmd_Argv(0));
424  return;
425  }
426 
427  char itemId[MAX_VAR * 2];
428  int amount = atoi(cgi->Cmd_Argv(2));
429  Q_strncpyz(itemId, cgi->Cmd_Argv(1), sizeof(itemId));
430 
431  if (Q_strstart(itemId, "aircraft_")) {
432  aircraft_t* aircraft = AIR_AircraftGetFromIDX(atoi(itemId + 9));
433  if (!aircraft)
434  return;
435  if (amount > 0) {
436  if (!TR_AircraftListSelect(aircraft))
437  return;
438 
439  /* Add aircraft */
440  cgi->LIST_AddPointer(&tr.aircraft, (void*)aircraft);
441 
442  /* Add pilot */
443  if (aircraft->pilot)
444  cgi->Cmd_ExecuteString("ui_trans_add ucn_%d 1", aircraft->pilot->chr.ucn);
445 
446  /* Add soldiers */
447  LIST_Foreach(aircraft->acTeam, Employee, employee) {
448  cgi->Cmd_ExecuteString("ui_trans_add ucn_%d 1", employee->chr.ucn);
449  }
450  } else if (amount < 0) {
451  /* Remove aircraft */
452  cgi->LIST_Remove(&tr.aircraft, (void*)aircraft);
453 
454  /* Remove pilot */
455  if (aircraft->pilot)
456  cgi->Cmd_ExecuteString("ui_trans_add ucn_%d -1", aircraft->pilot->chr.ucn);
457 
458  /* Remove soldiers */
459  LIST_Foreach(aircraft->acTeam, Employee, employee) {
460  cgi->Cmd_ExecuteString("ui_trans_add ucn_%d -1", employee->chr.ucn);
461  }
462  }
463  } else if (Q_strstart(itemId, "ucn_")) {
464  Employee* employee = E_GetEmployeeFromChrUCN(atoi(itemId + 4));
465  if (!employee)
466  return;
467 
468  if (amount > 0) {
469  if (!employee->isHiredInBase(base))
470  return;
471  if (cgi->LIST_GetPointer(tr.employees[employee->getType()], (void*)employee))
472  return;
473 
474  /* Add employee */
475  cgi->LIST_AddPointer(&tr.employees[employee->getType()], (void*)employee);
476 
477  /* Add inventory */
478  const Container* cont = nullptr;
479  while ((cont = employee->chr.inv.getNextCont(cont, true))) {
480  Item* ic = cont->getNextItem(nullptr);
481  while (ic) {
482  const Item item = *ic;
483  const objDef_t* od = item.def();
484  Item* next = ic->getNext();
485 
486  if (od)
487  cgi->Cmd_ExecuteString("ui_trans_add %s 1", od->id);
488  if (item.getAmmoLeft() && od->isReloadable()) {
489  const objDef_t* ammo = item.ammoDef();
490  if (ammo)
491  cgi->Cmd_ExecuteString("ui_trans_add %s 1", ammo->id);
492  }
493  ic = next;
494  }
495  }
496  } else if (amount < 0) {
497  /* Remove employee */
498  cgi->LIST_Remove(&tr.employees[employee->getType()], (void*)employee);
499 
500  /* Remove inventory */
501  const Container* cont = nullptr;
502  while ((cont = employee->chr.inv.getNextCont(cont, true))) {
503  Item* ic = cont->getNextItem(nullptr);
504  while (ic) {
505  const Item item = *ic;
506  const objDef_t* od = item.def();
507  Item* next = ic->getNext();
508 
509  if (od)
510  cgi->Cmd_ExecuteString("ui_trans_add %s -1", od->id);
511  if (item.getAmmoLeft() && od->isReloadable()) {
512  const objDef_t* ammo = item.ammoDef();
513  if (ammo)
514  cgi->Cmd_ExecuteString("ui_trans_add %s -1", ammo->id);
515  }
516  ic = next;
517  }
518  }
519  }
520  } else if (Q_streq(itemId, "scientist")) {
521  if (amount > 0) {
522  E_Foreach(EMPL_SCIENTIST, employee) {
523  if (amount == 0)
524  break;
525  if (!employee->isHiredInBase(base))
526  continue;
527  /* Already on transfer list. */
528  if (cgi->LIST_GetPointer(tr.employees[EMPL_SCIENTIST], (void*)employee))
529  continue;
530  cgi->LIST_AddPointer(&tr.employees[EMPL_SCIENTIST], (void*) employee);
531  amount--;
532  }
533  } else if (amount < 0) {
534  while (!cgi->LIST_IsEmpty(tr.employees[EMPL_SCIENTIST]) && amount < 0) {
535  if (cgi->LIST_RemoveEntry(&tr.employees[EMPL_SCIENTIST], tr.employees[EMPL_SCIENTIST]))
536  amount++;
537  }
538  }
539  } else if (Q_streq(itemId, "worker")) {
540  if (amount > 0) {
541  E_Foreach(EMPL_WORKER, employee) {
542  if (amount == 0)
543  break;
544  if (!employee->isHiredInBase(base))
545  continue;
546  /* Already on transfer list. */
547  if (cgi->LIST_GetPointer(tr.employees[EMPL_WORKER], (void*)employee))
548  continue;
549  cgi->LIST_AddPointer(&tr.employees[EMPL_WORKER], (void*) employee);
550  amount--;
551  }
552  } else if (amount < 0) {
553  while (!cgi->LIST_IsEmpty(tr.employees[EMPL_WORKER]) && amount < 0) {
554  if (cgi->LIST_RemoveEntry(&tr.employees[EMPL_WORKER], tr.employees[EMPL_WORKER]))
555  amount++;
556  }
557  }
558  } else if (Q_strstart(itemId, "alive_")) {
559  if (tr.alienCargo == nullptr)
560  tr.alienCargo = new AlienCargo();
561  if (tr.alienCargo == nullptr)
562  cgi->Com_Error(ERR_DROP, "TR_Add_f: Cannot create AlienCargo object\n");
563 
564  const teamDef_t* teamDef = cgi->Com_GetTeamDefinitionByID(itemId + 6);
565  if (teamDef && base->alienContainment) {
566  const int cargo = tr.alienCargo->getAlive(teamDef);
567  const int store = base->alienContainment->getAlive(teamDef);
568 
569  if (amount >= 0)
570  amount = std::min(amount, store - cargo);
571  else
572  amount = std::max(amount, -cargo);
573 
574  if (amount != 0)
575  tr.alienCargo->add(teamDef, amount, 0);
576  }
577  } else if (Q_strstart(itemId, "dead_")) {
578  if (tr.alienCargo == nullptr)
579  tr.alienCargo = new AlienCargo();
580  if (tr.alienCargo == nullptr)
581  cgi->Com_Error(ERR_DROP, "TR_Add_f: Cannot create AlienCargo object\n");
582 
583  const teamDef_t* teamDef = cgi->Com_GetTeamDefinitionByID(itemId + 5);
584  if (teamDef && base->alienContainment) {
585  const int cargo = tr.alienCargo->getDead(teamDef);
586  const int store = base->alienContainment->getDead(teamDef);
587 
588  if (amount >= 0)
589  amount = std::min(amount, store - cargo);
590  else
591  amount = std::max(amount, -cargo);
592 
593  if (amount != 0)
594  tr.alienCargo->add(teamDef, 0, amount);
595  }
596  } else if (Q_streq(itemId, ANTIMATTER_ITEM_ID)) {
597  /* antimatter */
598  const int cargo = tr.antimatter;
599  const int store = B_AntimatterInBase(base);
600 
601  if (amount >= 0)
602  amount = std::min(amount, store - cargo);
603  else
604  amount = std::max(amount, -cargo);
605  if (amount != 0)
606  tr.antimatter += amount;
607  } else {
608  /* items */
609  const objDef_t* od = INVSH_GetItemByID(itemId);
610  if (!od)
611  return;
613  return;
614 
615  if (tr.itemCargo == nullptr)
616  tr.itemCargo = new ItemCargo();
617  const int cargo = tr.itemCargo->getAmount(od);
618  const int store = B_ItemInBase(od, base);
619  if (amount >= 0)
620  amount = std::min(amount, store - cargo);
621  else
622  amount = std::max(amount, -cargo);
623  if (amount != 0)
624  tr.itemCargo->add(od, amount, 0);
625  }
626 
628  /* Update capacity list of destination base */
629  if (tr.destBase)
630  cgi->Cmd_ExecuteString("ui_trans_caplist %d", tr.destBase->idx);
631 }
632 
637 static void TR_TransferListClear_f (void)
638 {
640 
641  if (!base)
642  return;
643 
645 
646  /* Update cargo list and items list. */
647  TR_CargoList();
649 
650  /* Update capacity list of destination base */
651  if (tr.destBase)
652  cgi->Cmd_ExecuteString("ui_trans_caplist %d", tr.destBase->idx);
653 }
654 
661 static void TR_TransferBaseSelect (base_t* srcbase, base_t* destbase)
662 {
663  if (!destbase || !srcbase)
664  return;
665 
666  /* Set global pointer to current selected base. */
667  tr.destBase = destbase;
668  cgi->Cvar_Set("mn_trans_base_name", "%s", destbase->name);
669  cgi->Cvar_SetValue("mn_trans_base_id", destbase->idx);
670 
671  /* Update stuff-in-base list. */
672  TR_Fill(srcbase, destbase, currentTransferType);
673 
674  /* Update capacity list of destination base */
675  if (tr.destBase)
676  cgi->Cmd_ExecuteString("ui_trans_caplist %d", tr.destBase->idx);
677 }
678 
682 static void TR_InitBaseList (void)
683 {
684  const base_t* currentBase = B_GetCurrentSelectedBase();
685  uiNode_t* baseList = nullptr;
686  base_t* base = nullptr;
687 
688  while ((base = B_GetNext(base)) != nullptr) {
689  if (base == currentBase)
690  continue;
691 
692  cgi->UI_AddOption(&baseList, va("base%i", base->idx), base->name, va("%i", base->idx));
693  }
694 
695  cgi->UI_RegisterOption(OPTION_BASELIST, baseList);
696 }
697 
701 static void TR_SelectBase_f (void)
702 {
703  int baseIdx;
705  base_t* destbase;
706 
707  if (cgi->Cmd_Argc() < 2) {
708  cgi->Com_Printf("Usage: %s <baseIdx>\n", cgi->Cmd_Argv(0));
709  return;
710  }
711 
712  baseIdx = atoi(cgi->Cmd_Argv(1));
713  destbase = B_GetFoundedBaseByIDX(baseIdx);
714 
715  TR_TransferBaseSelect(base, destbase);
716 }
717 
723 static void TR_Init_f (void)
724 {
726 
728 
729  /* Update destination base list */
730  TR_InitBaseList();
731  /* Select first available base. */
732  tr.destBase = B_GetNext(base);
733  /* If this was the last base select the first */
734  if (!tr.destBase)
735  tr.destBase = B_GetNext(nullptr);
736  if (!tr.destBase)
737  cgi->Com_Error(ERR_DROP, "No bases! Transfer needs at least two...");
739  /* Set up cvar used to display transferBase. */
740  if (tr.destBase) {
741  cgi->Cvar_Set("mn_trans_base_name", "%s", tr.destBase->name);
742  cgi->Cvar_SetValue("mn_trans_base_id", tr.destBase->idx);
743  } else {
744  cgi->Cvar_Set("mn_trans_base_id", "");
745  }
746 
747  /* Set up cvar used with tabset */
748  cgi->Cvar_Set("mn_itemtype", "%s", transferTypeIDs[0]);
749  /* Select first available item */
750  cgi->Cmd_ExecuteString("ui_trans_fill %s", transferTypeIDs[0]);
751 }
752 
756 static void TR_TransferClose_f (void)
757 {
758  /* Unload what was loaded. */
760  /* Clear temporary cargo arrays. */
762 }
763 
767 static void TR_List_f (void)
768 {
769  int i = 0;
770 
771  cgi->UI_ExecuteConfunc("tr_listclear");
772  TR_Foreach(transfer) {
773  const char* source = transfer->srcBase ? transfer->srcBase->name : "mission";
774  const DateTime remainingTime = transfer->event - ccs.date;
775  cgi->UI_ExecuteConfunc("tr_listaddtransfer %d \"%s\" \"%s\" \"%s\"", ++i, source, transfer->destBase->name, CP_SecondConvert(Date_DateToSeconds(remainingTime)));
776 
777  /* Antimatter */
778  if (transfer->antimatter) {
779  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo", "antimatter", _("Antimatter"));
781  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo.antimatter", od->id, va("%i %s", transfer->antimatter, _(od->name)));
782  }
783  /* Items */
784  if (transfer->itemCargo != nullptr) {
785  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo", "items", _("Items"));
786  linkedList_t* cargo = transfer->itemCargo->list();
787  LIST_Foreach(cargo, itemCargo_t, item) {
788  if (item->amount <= 0)
789  continue;
790  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo.items", item->objDef->id, va("%i %s", item->amount, _(item->objDef->name)));
791  }
792  cgi->LIST_Delete(&cargo);
793  }
794  /* Employee */
795  if (transfer->hasEmployees) {
796  int j;
797  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo", "employee", _("Employee"));
798  for (j = EMPL_SOLDIER; j < MAX_EMPL; j++) {
799  const employeeType_t emplType = (employeeType_t)j;
800  TR_ForeachEmployee(employee, transfer, emplType) {
801  if (employee->getUGV()) {
803  } else {
804  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo.employee", va("ucn_%i", employee->chr.ucn), va("%s %s", E_GetEmployeeString(employee->getType(), 1), employee->chr.name));
805  }
806  }
807  }
808  }
809  /* Aliens */
810  if (transfer->alienCargo != nullptr) {
811  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo", "aliens", _("Aliens"));
812  linkedList_t* cargo = transfer->alienCargo->list();
814  if (item->alive > 0)
815  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo.aliens", va("%s_alive", item->teamDef->id), va("%i %s %s", item->alive, _("alive"), _(item->teamDef->name)));
816  if (item->dead > 0)
817  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo.aliens", va("%s_dead", item->teamDef->id), va("%i %s %s", item->dead, _("dead"), _(item->teamDef->name)));
818  }
819  cgi->LIST_Delete(&cargo);
820  }
821  /* Aircraft */
822  if (!cgi->LIST_IsEmpty(transfer->aircraft)) {
823  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo", "aircraft", _("Aircraft"));
824 
825  TR_ForeachAircraft(aircraft, transfer) {
826  cgi->UI_ExecuteConfunc("tr_listaddcargo %d \"%s\" \"%s\" \"%s\"", i, "tr_cargo.aircraft", va("craft%i", aircraft->idx), aircraft->name);
827  }
828  }
829  }
830 }
831 
837 static void TR_CountEmployeeInListArray (linkedList_t* employeeListArray[], int capacity[])
838 {
839  for (int i = EMPL_SOLDIER; i < EMPL_ROBOT; i++) {
840  capacity[CAP_EMPLOYEES] += cgi->LIST_Count(employeeListArray[i]);
841  }
842 }
843 
849 static void TR_CountAircraftInList (linkedList_t* aircraftList, int capacity[])
850 {
851  LIST_Foreach(aircraftList, aircraft_t, aircraft) {
852  capacity[AIR_GetHangarCapacityType(aircraft)]++;
853  }
854 }
855 
861 {
862  if (cgi->Cmd_Argc() < 2) {
863  cgi->Com_Printf("Usage: %s <destinationBaseIdx>\n", cgi->Cmd_Argv(0));
864  return;
865  }
866 
867  base_t* base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(1)));
868  if (!base) {
869  cgi->Com_Printf("Invalid destinationBaseIdx: %s\n", cgi->Cmd_Argv(1));
870  return;
871  }
872 
873  int currentCap[MAX_CAP];
874  OBJZERO(currentCap);
875 
876  /* Count capacity need of active transfers */
877  TR_Foreach(transfer) {
878  if (transfer->destBase != base)
879  continue;
880  /* - Antimatter */
881  currentCap[CAP_ANTIMATTER] += transfer->antimatter;
882  /* - Items */
883  currentCap[CAP_ITEMS] += transfer->itemCargo ? transfer->itemCargo->size() : 0;
884  /* - Employees in transit assinged to base already */
885  /* - Aliens */
886  currentCap[CAP_ALIENS] += (transfer->alienCargo) ? transfer->alienCargo->getAlive() : 0;
887  /* - Aircraft in transit are already assigned to their hangar! */
888  }
889 
890  /* Count capacity need of the current transfer plan */
891  /* - Antimatter */
892  currentCap[CAP_ANTIMATTER] += tr.antimatter;
893  /* - Items */
894  currentCap[CAP_ITEMS] += tr.itemCargo ? tr.itemCargo->size() : 0;
895  /* - Employee */
897  /* - Aliens */
898  currentCap[CAP_ALIENS] += tr.alienCargo ? tr.alienCargo->getAlive() : 0;
899  /* - Aircraft */
900  TR_CountAircraftInList(tr.aircraft, currentCap);
901 
902  cgi->UI_ExecuteConfunc("ui_t_capacities_clear");
903  for (int i = 0; i < ccs.numBuildingTemplates; i++) {
904  const building_t* building = &ccs.buildingTemplates[i];
906 
907  /* skip not transferable capacities */
908  if (capType == MAX_CAP || capType == CAP_LABSPACE || capType == CAP_WORKSPACE)
909  continue;
910  /* show only researched buildings' */
911  if (!RS_IsResearched_ptr(building->tech))
912  continue;
913 
914  capacities_t cap = *CAP_Get(base, capType);
915  currentCap[capType] += cap.cur;
916  if (cap.max <= 0 && currentCap[capType] <= 0)
917  continue;
918 
919  cgi->UI_ExecuteConfunc("ui_t_capacities_add \"%s\" \"%s\" %d %d", building->id, _(building->name), currentCap[capType], cap.max);
920  }
921 }
922 
923 static const cmdList_t transferCallbacks[] = {
924  {"trans_list", TR_List_f, "Assembles the transferlist"},
925  {"trans_init", TR_Init_f, "Init function for Transfer menu"},
926  {"trans_close", TR_TransferClose_f, "Callback for closing Transfer Menu"},
927  {"trans_start", TR_TransferStart_f, "Starts the transfer"},
928  {"trans_emptyairstorage", TR_TransferListClear_f, "Unload everything from transfer cargo back to base"},
929  {"trans_selectbase", TR_SelectBase_f, "Callback for selecting a base"},
930  {"ui_trans_caplist", TR_DestinationCapacityList_f, "Update destination base capacity list"},
931  {"ui_trans_fill", TR_Fill_f, "Fill itemlists for transfer"},
932  {"ui_trans_add", TR_Add_f, "Add/Remove items to transfercargo"},
933  {nullptr, nullptr, nullptr}
934 };
935 void TR_InitCallbacks (void)
936 {
937  cgi->Cmd_TableAddList(transferCallbacks);
938 }
939 
941 {
943 
944  cgi->Cmd_TableRemoveList(transferCallbacks);
945 }
Transfer information (they are being stored in ccs.transfers).
Definition: cp_transfer.h:33
static void TR_Init_f(void)
Transfer menu init function.
const char * name
Definition: inv_shared.h:267
static void TR_CargoList(void)
Display cargo list.
int B_ItemInBase(const objDef_t *item, const base_t *base)
Check if the item has been collected (i.e it is in the storage) in the given base.
Definition: cp_base.cpp:2133
building_t buildingTemplates[MAX_BUILDINGS]
Definition: cp_campaign.h:339
linkedList_t * employees[MAX_EMPL]
Definition: cp_transfer.h:41
bool AIR_IsAircraftInBase(const aircraft_t *aircraft)
Checks whether given aircraft is in its homebase.
virtual bool add(const objDef_t *od, int amount, int looseAmount)
Add items to the cargo.
Definition: itemcargo.cpp:39
csi_t * csi
Definition: cgame.h:100
uiMessageListNodeMessage_t * MSO_CheckAddNewMessage(const notify_t messagecategory, const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup)
Adds a new message to message stack. It uses message settings to verify whether sound should be playe...
class ItemCargo * itemCargo
Definition: cp_transfer.h:39
Store capacities in base.
Definition: cp_capacity.h:41
class AlienCargo * alienCargo
Definition: cp_transfer.h:40
virtual bool add(const teamDef_t *team, int alive, int dead)
Add aliens to the cargo by teamDef.
Definition: aliencargo.cpp:38
bool RS_IsResearched_ptr(const technology_t *tech)
Checks whether an item is already researched.
const objDef_t * INVSH_GetItemByID(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn&#39;t found.
Definition: inv_shared.cpp:282
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
Item * getNextItem(const Item *prev) const
Definition: inv_shared.cpp:671
const char * shortname
Definition: cp_rank.h:32
Header file for menu related console command callbacks.
static void TR_Add_f(void)
Callback handles adding/removing items to transfercargo.
cvar_t *IMPORT * Cvar_Set(const char *varName, const char *value,...) __attribute__((format(__printf__
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
#define E_Foreach(employeeType, var)
Definition: cp_employee.h:122
linkedList_t * list(void) const
Returns a copy of the cargo list.
Definition: aliencargo.cpp:150
#define _(String)
Definition: cl_shared.h:44
static void TR_List_f(void)
Assembles the list of transfers for the popup.
int Date_DateToSeconds(const DateTime &date)
Convert a date to seconds.
Definition: cp_time.cpp:228
struct technology_s * tech
Definition: cp_building.h:111
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
static void TR_FillItems(const base_t *srcBase, const base_t *destBase)
Add items to the transfer storages list.
#define CAP_Get(base, capacity)
Capacity macros.
Definition: cp_capacity.h:50
base_t * destBase
Definition: cp_transfer.h:34
Class describing a point of time.
Definition: DateTime.h:30
base_t * B_GetCurrentSelectedBase(void)
returns the currently selected base
Definition: cp_base.cpp:1578
character_t chr
Definition: cp_employee.h:119
char name[MAX_VAR]
Definition: cp_base.h:86
aircraft_t * AIR_AircraftGetFromIDX(int aircraftIdx)
Returns aircraft for a given global index.
static bool TR_AircraftListSelect(const aircraft_t *aircraft)
Check if an aircraft should be displayed for transfer.
Header file for Transfer stuff.
static const cmdList_t transferCallbacks[]
const char *IMPORT * Cmd_Argv(int n)
rank_t * CL_GetRankByIdx(const int index)
Returns a rank at an index.
Definition: cp_rank.cpp:50
Item cargo class header.
static transferType_t TR_GetTransferType(const char *id)
Returns the transfer type.
baseCapacities_t AIR_GetHangarCapacityType(const aircraft_t *aircraft)
Returns capacity type needed for an aircraft.
int size(void) const
Calculate size of all items in the cargo.
Definition: itemcargo.cpp:184
const char * id
Definition: inv_shared.h:268
int E_CountHired(const base_t *const base, employeeType_t type)
Counts hired employees of a given type in a given base.
static void TR_Fill(const base_t *srcBase, const base_t *destBase, transferType_t transferType)
Fills the items-in-base list with stuff available for transfer.
base_t * B_GetFoundedBaseByIDX(int baseIdx)
Array bound check for the base index.
Definition: cp_base.cpp:326
int getAlive(const teamDef_t *team) const
Return number of alive aliens of a type in the cargo.
Definition: aliencargo.cpp:100
int idx
Definition: cp_base.h:85
bool isHiredInBase(const base_t *const base) const
Checks whether the given employee is in the given base.
Definition: cp_employee.h:94
linkedList_t * cargo
Definition: aliencargo.h:43
static void TR_InitBaseList(void)
Fills the optionlist with available bases to transfer to.
item instance data, with linked list capability
Definition: inv_shared.h:402
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
static void TR_FillAliens(const base_t *srcBase, const base_t *destBase)
Add aliens to the transfer storages list.
int getDead(const teamDef_t *team) const
Return number of dead alien bodies of a type in the cargo.
Definition: aliencargo.cpp:117
#define ERR_DROP
Definition: common.h:211
int numBuildingTemplates
Definition: cp_campaign.h:340
#define TR_Foreach(var)
Definition: cp_transfer.h:48
#define OBJZERO(obj)
Definition: shared.h:178
employeeType_t getType() const
Definition: cp_employee.h:99
linkedList_t * aircraft
Definition: cp_transfer.h:42
#define MAX_VAR
Definition: shared.h:36
transferType_t
transfer types
Item cargo class.
Definition: itemcargo.h:41
Item * getNext() const
Definition: inv_shared.h:451
static void TR_CountAircraftInList(linkedList_t *aircraftList, int capacity[])
Count capacity need of aircraft in lists.
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition: cp_base.cpp:286
const cgame_import_t * cgi
static void TR_CountEmployeeInListArray(linkedList_t *employeeListArray[], int capacity[])
Count capacity need of employee in array of lists.
const char uiNode_t *IMPORT * UI_AddOption(uiNode_t **tree, const char *name, const char *label, const char *value)
employeeType_t
The types of employees.
Definition: cp_employee.h:30
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition: shared.cpp:587
static void TR_Fill_f(void)
Callback for filling list with stuff available for transfer.
ccs_t ccs
Definition: cp_campaign.cpp:63
static transfer_t tr
int numODs
Definition: q_shared.h:518
Campaign geoscape time header.
const teamDef_t *IMPORT * Com_GetTeamDefinitionByID(const char *team)
static void TR_FillEmployees(const base_t *srcBase, const base_t *destBase)
Add employees to the transfer storages list.
bool isReloadable() const
Definition: inv_shared.h:352
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
char * name
Definition: cp_building.h:79
Alien containment class header.
static void TR_TransferStart_f(void)
Starts the transfer.
static transferType_t currentTransferType
#define AIR_ForeachFromBase(var, base)
iterates trough all aircraft from a specific homebase
Definition: cp_aircraft.h:202
alien cargo entry
Definition: aliencargo.h:32
linkedList_t *IMPORT * LIST_GetPointer(linkedList_t *list, const void *data)
baseCapacities_t
All possible capacities in base.
Definition: cp_capacity.h:27
An aircraft with all it&#39;s data.
Definition: cp_aircraft.h:115
AlienCargo(void)
Creates and initializes AlienCargo object.
Definition: aliencargo.cpp:206
transfer_t * TR_TransferStart(base_t *srcBase, transfer_t &transData)
Starts a transfer.
int B_AntimatterInBase(const base_t *base)
returns the amount of antimatter stored in a base
Definition: cp_base.cpp:2613
#define TR_ForeachEmployee(var, transfer, employeeType)
Definition: cp_transfer.h:49
QGL_EXTERN GLint i
Definition: r_gl.h:113
item cargo entry
Definition: itemcargo.h:32
void TR_InitCallbacks(void)
CASSERT(lengthof(transferTypeIDs)==TRANS_TYPE_MAX)
class DateTime date
Definition: cp_campaign.h:246
const char * id
Definition: cp_building.h:78
Definition: cmd.h:86
Employee * E_GetEmployeeFromChrUCN(int uniqueCharacterNumber)
Searches all employee for the ucn (character id)
int antimatter
Definition: cp_transfer.h:38
const char * E_GetEmployeeString(employeeType_t type, int n)
Convert employeeType_t to translated string.
static void TR_TransferListClear_f(void)
Unload everything from transfer cargo back to base.
static char const *const transferTypeIDs[]
transfer typeID strings
Alien cargo class header.
int getAmount(const objDef_t *od) const
Returns amount of an item in the cargo.
Definition: itemcargo.cpp:131
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it&#39;s safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
buildingType_t buildingType
Definition: cp_building.h:110
Header file for single player campaign control.
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
Definition: inv_shared.cpp:266
static void TR_DestinationCapacityList_f(void)
Callback for assemble destination base capacity list.
#define lengthof(x)
Definition: shared.h:105
static void TR_ClearTempCargo(void)
Clear temporary cargo arrays.
A building with all it&#39;s data.
Definition: cp_building.h:73
#define Q_streq(a, b)
Definition: shared.h:136
static void TR_TransferClose_f(void)
Closes Transfer Menu and resets temp arrays.
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
Definition: inv_shared.cpp:722
#define TR_ForeachAircraft(var, transfer)
Definition: cp_transfer.h:50
class AlienContainment * alienContainment
Definition: cp_base.h:108
const objDef_t * def(void) const
Definition: inv_shared.h:469
Describes a rank that a recruit can gain.
Definition: cp_rank.h:29
static void TR_SelectBase_f(void)
Callback to select destination base.
#define ANTIMATTER_ITEM_ID
Definition: cp_research.h:37
baseCapacities_t B_GetCapacityFromBuildingType(buildingType_t type)
Get the capacity associated to a building type.
Definition: cp_base.cpp:416
linkedList_t * list(void) const
Returns a copy of the cargo list.
Definition: itemcargo.cpp:156
const char * CP_SecondConvert(int second)
Converts a number of second into a char to display.
Definition: cp_time.cpp:57
void TR_ShutdownCallbacks(void)
int getAmmoLeft() const
Definition: inv_shared.h:466
static void TR_TransferBaseSelect(base_t *srcbase, base_t *destbase)
Callback for base list click.
static void TR_FillAircraft(const base_t *srcBase, const base_t *destBase)
Add aircraft to the transfer storages list.
Inventory inv
Definition: chr_shared.h:411
bool B_ItemIsStoredInBaseStorage(const objDef_t *obj)
Check if an item is stored in storage.
Definition: cp_base.cpp:2560