UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cp_base.cpp
Go to the documentation of this file.
1 
8 /*
9 Copyright (C) 2002-2020 UFO: Alien Invasion.
10 
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 See the GNU General Public License for more details.
21 
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26 
27 #include "../../cl_shared.h"
28 #include "../../cl_inventory.h" /* INV_GetEquipmentDefinitionByID */
29 #include "../../ui/ui_dataids.h"
30 #include "../../../shared/parse.h"
31 #include "cp_campaign.h"
32 #include "cp_mapfightequip.h"
33 #include "cp_aircraft.h"
34 #include "cp_missions.h"
35 #include "cp_geoscape.h"
36 #include "cp_popup.h"
37 #include "cp_radar.h"
38 #include "cp_time.h"
39 #include "cp_base_callbacks.h"
40 #include "cp_ufo.h"
41 #include "save/save_base.h"
42 #include "aliencontainment.h"
43 #include "itemcargo.h"
44 
45 #define B_GetBuildingByIDX(baseIdx, buildingIdx) (&ccs.buildings[(baseIdx)][(buildingIdx)])
46 #define B_GetBuildingIDX(base, building) ((ptrdiff_t)((building) - ccs.buildings[base->idx]))
47 #define B_GetBaseIDX(base) ((ptrdiff_t)((base) - ccs.bases))
48 
49 static void B_InitialEquipment(aircraft_t* aircraft, const equipDef_t* ed);
50 
57 static linkedList_t* B_GetNeighbours (const building_t* building)
58 {
59  if (!building || !B_IsBuildingBuiltUp(building))
60  return nullptr;
61 
62  const int x = building->pos[0];
63  const int y = building->pos[1];
64  base_t* base = building->base;
65  linkedList_t* neighbours = nullptr;
66 
67  assert(base);
68  for (int i = x; i < x + building->size[0]; i++) {
69  /* North */
70  if (y > 0 && B_GetBuildingAt(base, i, y - 1) != nullptr)
71  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, i, y - 1));
72  /* South */
73  if (y < BASE_SIZE - building->size[1] && B_GetBuildingAt(base, i, y + building->size[1]) != nullptr)
74  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, i, y + building->size[1]));
75  }
76  for (int i = y; i < y + building->size[1]; i++) {
77  /* West */
78  if (x > 0 && B_GetBuildingAt(base, x - 1, i) != nullptr)
79  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, x - 1, i));
80  /* East */
81  if (x < BASE_SIZE - building->size[0] && B_GetBuildingAt(base, x + building->size[0], i) != nullptr)
82  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, x + building->size[0], i));
83  }
84  return neighbours;
85 }
86 
87 #ifdef DEBUG
88 
92 static bool B_IsCoherent (const base_t* base)
93 {
94  int found[MAX_BUILDINGS];
95  linkedList_t* queue = nullptr;
96  building_t* bldg = nullptr;
97 
98  OBJZERO(found);
99  while ((bldg = B_GetNextBuilding(base, bldg)) != nullptr) {
100  cgi->LIST_AddPointer(&queue, (void*)bldg);
101  break;
102  }
103  if (!bldg)
104  return true;
105 
106  while (!cgi->LIST_IsEmpty(queue)) {
107  bldg = (building_t*)queue->data;
108  found[bldg->idx] = 1;
109  cgi->LIST_RemoveEntry(&queue, queue);
110 
111  linkedList_t* neighbours = B_GetNeighbours(bldg);
112  LIST_Foreach(neighbours, building_t, bldg) {
113  if (found[bldg->idx] == 0) {
114  found[bldg->idx] = 1;
115  cgi->LIST_AddPointer(&queue, (void*)bldg);
116  }
117  }
118  cgi->LIST_Delete(&neighbours);
119  }
120  cgi->LIST_Delete(&queue);
121 
122  for (int i = 0; i < ccs.numBuildings[base->idx]; i++) {
123  if (found[i] != 1)
124  return false;
125  }
126  return true;
127 }
128 #endif
129 
137 static bool B_AddBlockedTile (base_t* base, int row, int column)
138 {
139  assert(base);
140 
141  if (B_GetBuildingAt(base, column, row) != nullptr)
142  return false;
143 
144  int found[BASE_SIZE][BASE_SIZE];
145  OBJZERO(found);
146  found[row][column] = -1;
147 
148  linkedList_t* queue = nullptr;
149  /* Get first non blocked tile */
150  for (int y = 0; y < BASE_SIZE && cgi->LIST_IsEmpty(queue); y++) {
151  for (int x = 0; x < BASE_SIZE && cgi->LIST_IsEmpty(queue); x++) {
152  if (x == column && y == row)
153  continue;
154  if (!B_IsTileBlocked(base, x, y))
155  cgi->LIST_AddPointer(&queue, &base->map[y][x]);
156  }
157  }
158 
159  if (cgi->LIST_IsEmpty(queue))
160  return false;
161 
162  /* BS Traversal */
163  while (!cgi->LIST_IsEmpty(queue)) {
164  baseBuildingTile_t* tile = (baseBuildingTile_t*)queue->data;
165  cgi->LIST_RemoveEntry(&queue, queue);
166  const int x = tile->posX;
167  const int y = tile->posY;
168 
169  found[y][x] = 1;
170  /* West */
171  if (x > 0 && !B_IsTileBlocked(base, x - 1, y) && found[y][x - 1] == 0)
172  cgi->LIST_AddPointer(&queue, (void*)&base->map[y][x - 1]);
173  /* East */
174  if (x < BASE_SIZE - 1 && !B_IsTileBlocked(base, x + 1, y) && found[y][x + 1] == 0)
175  cgi->LIST_AddPointer(&queue, (void*)&base->map[y][x + 1]);
176  /* North */
177  if (y > 0 && !B_IsTileBlocked(base, x, y - 1) && found[y - 1][x] == 0)
178  cgi->LIST_AddPointer(&queue, (void*)&base->map[y - 1][x]);
179  /* South */
180  if (y < BASE_SIZE - 1 && !B_IsTileBlocked(base, x, y + 1) && found[y + 1][x] == 0)
181  cgi->LIST_AddPointer(&queue, (void*)&base->map[y + 1][x]);
182  }
183  cgi->LIST_Delete(&queue);
184 
185  /* Check for unreached areas */
186  for (int y = 0; y < BASE_SIZE; y++) {
187  for (int x = 0; x < BASE_SIZE; x++) {
188  if (!B_IsTileBlocked(base, x, y) && found[y][x] == 0)
189  return false;
190  }
191  }
192  base->map[row][column].blocked = true;
193  return true;
194 }
195 
201 static void B_AddBlockedTiles (base_t* base, int count)
202 {
203  assert(base);
204 
205  for (int placed = 0; placed < count; placed++) {
206  const int x = rand() % BASE_SIZE;
207  const int y = rand() % BASE_SIZE;
208 
209  if (B_IsTileBlocked(base, x, y))
210  continue;
211 
212  if (B_GetBuildingAt(base, x, y) != nullptr)
213  continue;
214 
215  B_AddBlockedTile(base, y, x);
216  }
217 }
218 
223 bool B_IsBuildingDestroyable (const building_t* building)
224 {
225  assert(building);
226  base_t* base = building->base;
227  assert(base);
228 
229  if (base->baseStatus == BASE_DESTROYED)
230  return true;
231 
232  linkedList_t* queue = nullptr;
233  building_t* bldg = nullptr;
234 
235  while ((bldg = B_GetNextBuilding(base, bldg)) != nullptr) {
236  if (bldg != building) {
237  cgi->LIST_AddPointer(&queue, (void*)bldg);
238  break;
239  }
240  }
241  if (!bldg)
242  return true;
243 
244  int found[MAX_BUILDINGS];
245  OBJZERO(found);
246  /* prevents adding building to be removed to the queue */
247  found[building->idx] = 1;
248 
249  while (!cgi->LIST_IsEmpty(queue)) {
250  bldg = (building_t*)queue->data;
251  found[bldg->idx] = 1;
252  cgi->LIST_RemoveEntry(&queue, queue);
253 
254  linkedList_t* neighbours = B_GetNeighbours(bldg);
255  LIST_Foreach(neighbours, building_t, bldg) {
256  if (found[bldg->idx] == 0) {
257  found[bldg->idx] = 1;
258  cgi->LIST_AddPointer(&queue, (void*)bldg);
259  }
260  }
261  cgi->LIST_Delete(&neighbours);
262  }
263  cgi->LIST_Delete(&queue);
264 
265  for (int i = 0; i < ccs.numBuildings[base->idx]; i++) {
266  if (found[i] != 1)
267  return false;
268  }
269 
270  return true;
271 }
272 
276 int B_GetCount (void)
277 {
278  return ccs.numBases;
279 }
280 
285 base_t* B_GetNext (base_t* lastBase)
286 {
287  base_t* endOfBases = &ccs.bases[B_GetCount()];
288  base_t* base;
289 
290  if (!B_GetCount())
291  return nullptr;
292 
293  if (!lastBase)
294  return ccs.bases;
295  assert(lastBase >= ccs.bases);
296  assert(lastBase < endOfBases);
297 
298  base = lastBase;
299 
300  base++;
301  if (base >= endOfBases)
302  return nullptr;
303  return base;
304 }
305 
312 base_t* B_GetBaseByIDX (int baseIdx)
313 {
314  if (baseIdx >= MAX_BASES || baseIdx < 0)
315  return nullptr;
316 
317  return &ccs.bases[baseIdx];
318 }
319 
326 {
327  if (baseIdx >= B_GetCount())
328  return nullptr;
329 
330  return B_GetBaseByIDX(baseIdx);
331 }
332 
338 building_t* B_GetNextBuilding (const base_t* base, building_t* lastBuilding)
339 {
340  building_t* endOfBuildings = B_GetBuildingByIDX(base->idx, ccs.numBuildings[base->idx]);
341  building_t* building;
342 
343  if (!ccs.numBuildings[base->idx])
344  return nullptr;
345 
346  if (!lastBuilding)
347  return ccs.buildings[base->idx];
348  assert(lastBuilding >= ccs.buildings[base->idx]);
349  assert(lastBuilding < endOfBuildings);
350 
351  building = lastBuilding;
352 
353  building++;
354  if (building >= endOfBuildings)
355  return nullptr;
356  return building;
357 }
358 
366 building_t* B_GetNextBuildingByType (const base_t* base, building_t* lastBuilding, buildingType_t buildingType)
367 {
368  building_t* building = lastBuilding;
369 
370  while ((building = B_GetNextBuilding(base, building))) {
371  if (building->buildingType == buildingType)
372  break;
373  }
374  return building;
375 }
376 
389 bool B_CheckBuildingTypeStatus (const base_t* const base, buildingType_t type, buildingStatus_t status, int* cnt)
390 {
391  int cntlocal = 0;
392  building_t* building = nullptr;
393 
394  while ((building = B_GetNextBuildingByType(base, building, type))) {
395  if (building->buildingStatus != status)
396  continue;
397  cntlocal++;
398  /* don't count any further - the caller doesn't want to know the value */
399  if (!cnt)
400  return true;
401  }
402 
403  /* set the cnt pointer if the caller wants to know this value */
404  if (cnt)
405  *cnt = cntlocal;
406 
407  return cntlocal ? true : false;
408 }
409 
416 {
417  switch (type) {
418  case B_LAB:
419  return CAP_LABSPACE;
420  case B_QUARTERS:
421  return CAP_EMPLOYEES;
422  case B_STORAGE:
423  return CAP_ITEMS;
424  case B_WORKSHOP:
425  return CAP_WORKSPACE;
426  case B_HANGAR:
427  return CAP_AIRCRAFT_BIG;
428  case B_ALIEN_CONTAINMENT:
429  return CAP_ALIENS;
430  case B_SMALL_HANGAR:
431  return CAP_AIRCRAFT_SMALL;
432  case B_ANTIMATTER:
433  return CAP_ANTIMATTER;
434  default:
435  return MAX_CAP;
436  }
437 }
438 
447 {
448  switch (cap) {
449  case CAP_ALIENS:
450  return B_ALIEN_CONTAINMENT;
451  case CAP_AIRCRAFT_SMALL:
452  return B_SMALL_HANGAR;
453  case CAP_AIRCRAFT_BIG:
454  return B_HANGAR;
455  case CAP_EMPLOYEES:
456  return B_QUARTERS;
457  case CAP_ITEMS:
458  return B_STORAGE;
459  case CAP_LABSPACE:
460  return B_LAB;
461  case CAP_WORKSPACE:
462  return B_WORKSHOP;
463  case CAP_ANTIMATTER:
464  return B_ANTIMATTER;
465  default:
466  return MAX_BUILDING_TYPE;
467  }
468 }
469 
477 bool B_GetBuildingStatus (const base_t* const base, const buildingType_t buildingType)
478 {
479  assert(base);
480 
481  if (buildingType == B_MISC)
482  return true;
483 
484  if (buildingType < MAX_BUILDING_TYPE)
485  return base->hasBuilding[buildingType];
486 
487  cgi->Com_Printf("B_GetBuildingStatus: Building-type %i does not exist.\n", buildingType);
488  return false;
489 }
490 
498 void B_SetBuildingStatus (base_t* const base, const buildingType_t buildingType, bool newStatus)
499 {
500  if (buildingType == B_MISC) {
501  cgi->Com_Printf("B_SetBuildingStatus: No status is associated to B_MISC type of building.\n");
502  } else if (buildingType < MAX_BUILDING_TYPE) {
503  assert(base);
504  base->hasBuilding[buildingType] = newStatus;
505  cgi->Com_DPrintf(DEBUG_CLIENT, "B_SetBuildingStatus: set status for %i to %i\n", buildingType, newStatus);
506  } else {
507  cgi->Com_Printf("B_SetBuildingStatus: Type of building %i does not exist\n", buildingType);
508  }
509 }
510 
519 {
520  float max = 0.0f;
521 
522  if (B_GetBuildingStatus(base, type)) {
523  building_t* building = nullptr;
524  while ((building = B_GetNextBuildingByType(base, building, type)))
525  if (building->buildingStatus == B_STATUS_WORKING)
526  max = std::max(building->level, max);
527  }
528 
529  return max;
530 }
531 
544 static inline void B_AddMap (char* maps, size_t mapsLength, char* coords, size_t coordsLength, const char* map, int col, int row)
545 {
546  Q_strcat(coords, coordsLength, "%i %i %i ", col * BASE_TILE_UNITS, (BASE_SIZE - row - 1) * BASE_TILE_UNITS, 0);
547  Q_strcat(maps, mapsLength, "%s", map);
548 }
549 
562 bool B_AssembleMap (char* maps, size_t mapsLength, char* coords, size_t coordsLength, const base_t* base)
563 {
564  if (!base) {
565  cgi->Com_Printf("B_AssembleMap: No base to assemble\n");
566  return false;
567  }
568 
569  maps[0] = '\0';
570  coords[0] = '\0';
571 
572  for (int row = 0; row < BASE_SIZE; row++) {
573  for (int col = 0; col < BASE_SIZE; col++) {
574  const building_t* building = B_GetBuildingAt(base, col, row);
575  if (!building) {
576  B_AddMap(maps, mapsLength, coords, coordsLength, "b/empty ", col, row);
577  continue;
578  }
579  if (building->pos[0] != col || building->pos[1] != row)
580  continue;
581  if (!B_IsBuildingBuiltUp(building)) {
582  B_AddMap(maps, mapsLength, coords, coordsLength, "b/construction ", col, row);
583  continue;
584  }
585  if (!building->mapPart)
586  cgi->Com_Error(ERR_DROP, "MapPart for building '%s' is missing'", building->id);
587  B_AddMap(maps, mapsLength, coords, coordsLength, va("b/%s ", building->mapPart), col, row);
588  }
589  }
590 
591  return true;
592 }
593 
599 static bool B_CheckUpdateBuilding (building_t* building)
600 {
601  bool oldValue;
602 
603  assert(building);
604 
605  /* Status of Miscellenious buildings cannot change. */
606  if (building->buildingType == B_MISC)
607  return false;
608 
609  oldValue = B_GetBuildingStatus(building->base, building->buildingType);
610  if (building->buildingStatus == B_STATUS_WORKING
612  B_SetBuildingStatus(building->base, building->buildingType, true);
613  else
614  B_SetBuildingStatus(building->base, building->buildingType, false);
615 
616  if (B_GetBuildingStatus(building->base, building->buildingType) != oldValue) {
617  cgi->Com_DPrintf(DEBUG_CLIENT, "Status of building %s is changed to %i.\n",
618  building->name, B_GetBuildingStatus(building->base, building->buildingType));
619  return true;
620  }
621 
622  return false;
623 }
624 
634 static bool B_UpdateStatusBuilding (base_t* base, buildingType_t buildingType, bool onBuilt)
635 {
636  bool test = false;
637  bool returnValue = false;
638  building_t* building = nullptr;
639 
640  /* Construction / destruction may have changed the status of other building
641  * We check that, but only for buildings which needed building */
642  while ((building = B_GetNextBuilding(base, building))) {
643  const building_t* dependsBuilding = building->dependsBuilding;
644  if (dependsBuilding && buildingType == dependsBuilding->buildingType) {
645  /* ccs.buildings[base->idx][i] needs built/removed building */
646  if (onBuilt && !B_GetBuildingStatus(base, building->buildingType)) {
647  /* we can only activate a non operational building */
648  if (B_CheckUpdateBuilding(building)) {
649  B_FireEvent(building, base, B_ONENABLE);
650  test = true;
651  returnValue = true;
652  }
653  } else if (!onBuilt && B_GetBuildingStatus(base, building->buildingType)) {
654  /* we can only deactivate an operational building */
655  if (B_CheckUpdateBuilding(building)) {
656  B_FireEvent(building, base, B_ONDISABLE);
657  test = true;
658  returnValue = true;
659  }
660  }
661  }
662  }
663  /* and maybe some updated status have changed status of other building.
664  * So we check again, until nothing changes. (no condition here for check, it's too complex) */
665  while (test) {
666  test = false;
667  building = nullptr;
668  while ((building = B_GetNextBuilding(base, building))) {
669  if (onBuilt && !B_GetBuildingStatus(base, building->buildingType)) {
670  /* we can only activate a non operational building */
671  if (B_CheckUpdateBuilding(building)) {
672  B_FireEvent(building, base, B_ONENABLE);
673  test = true;
674  }
675  } else if (!onBuilt && B_GetBuildingStatus(base, building->buildingType)) {
676  /* we can only deactivate an operational building */
677  if (B_CheckUpdateBuilding(building)) {
678  B_FireEvent(building, base, B_ONDISABLE);
679  test = true;
680  }
681  }
682  }
683  }
684 
685  return returnValue;
686 }
687 
693 static void B_UpdateAntimatterCap (base_t* base)
694 {
696  if (od != nullptr)
697  CAP_SetCurrent(base, CAP_ANTIMATTER, B_ItemInBase(od, base));
698 }
699 
705 void B_ResetAllStatusAndCapacities (base_t* base, bool firstEnable)
706 {
707  bool test = true;
708 
709  assert(base);
710 
711  cgi->Com_DPrintf(DEBUG_CLIENT, "Reseting base %s:\n", base->name);
712 
713  /* reset all values of hasBuilding[] */
714  for (int i = 0; i < MAX_BUILDING_TYPE; i++) {
716  if (type != B_MISC)
717  B_SetBuildingStatus(base, type, false);
718  }
719  /* activate all buildings that needs to be activated */
720  while (test) {
721  building_t* building = nullptr;
722  test = false;
723  while ((building = B_GetNextBuilding(base, building))) {
724  if (!B_GetBuildingStatus(base, building->buildingType)
725  && B_CheckUpdateBuilding(building)) {
726  if (firstEnable)
727  B_FireEvent(building, base, B_ONENABLE);
728  test = true;
729  }
730  }
731  }
732 
733  /* Update all capacities of base */
735 
736  /* calculate capacities.cur for every capacity */
737 
738  /* Current CAP_ALIENS (live alien capacity) is managed by AlienContainment class */
739  /* Current aircraft capacities should not need recounting */
740 
742  CAP_SetCurrent(base, CAP_EMPLOYEES, E_CountAllHired(base, true));
743 
745  CAP_UpdateStorageCap(base);
746 
749 
752 
754  B_UpdateAntimatterCap(base);
755 
756  /* Check that current capacity is possible -- if we changed values in *.ufo */
757  for (int i = 0; i < MAX_CAP; i++) {
758  const baseCapacities_t cap = (baseCapacities_t)i;
759  if (CAP_GetFreeCapacity(base, cap) < 0)
760  cgi->Com_Printf("B_ResetAllStatusAndCapacities: Warning, capacity of %i is bigger than maximum capacity\n", i);
761  }
762 }
763 
771 {
772  /* Don't allow to destroy a mandatory building. */
773  if (building->mandatory)
774  return false;
775 
776  base_t* base = building->base;
777 
778  if (base->map[(int)building->pos[1]][(int)building->pos[0]].building != building) {
779  cgi->Com_Error(ERR_DROP, "B_BuildingDestroy: building mismatch at base %i pos %i,%i.",
780  base->idx, (int)building->pos[0], (int)building->pos[1]);
781  }
782 
783  /* Refuse destroying if it hurts coherency - only exception is when the whole base destroys */
784  if (!B_IsBuildingDestroyable(building)) {
785  return false;
786  }
787 
788  /* liquidation rate gives back percentage of the worth of a building when it's recycled. Making replacing buildings more practical */
790 
791  const buildingType_t buildingType = building->buildingType;
792  const building_t* buildingTemplate = building->tpl;
793  const bool runDisableCommand = building->buildingStatus == B_STATUS_WORKING;
794  building->buildingStatus = B_STATUS_NOT_SET;
795 
796  int const baseIDX = base->idx;
797  building_t* const buildings = ccs.buildings[baseIDX];
798  int const idx = building->idx;
799 
800  for (int y = building->pos[1]; y < building->pos[1] + building->size[1]; y++)
801  for (int x = building->pos[0]; x < building->pos[0] + building->size[0]; x++)
802  base->map[y][x].building = nullptr;
803 
804  REMOVE_ELEM(buildings, idx, ccs.numBuildings[baseIDX]);
805 
806  /* Update the link of other buildings */
807  const int cntBldgs = ccs.numBuildings[baseIDX];
808  for (int i = 0; i < cntBldgs; i++) {
809  building_t* bldg = &buildings[i];
810  if (bldg->idx < idx)
811  continue;
812  bldg->idx--;
813 
814  for (int y = bldg->pos[1]; y < bldg->pos[1] + bldg->size[1]; y++)
815  for (int x = (int)bldg->pos[0]; x < bldg->pos[0] + bldg->size[0]; x++)
816  base->map[y][x].building = bldg;
817  }
818  building = nullptr;
819 
820  /* Don't use the building pointer after this point - it's zeroed. */
821 
822  if (buildingType != B_MISC && buildingType != MAX_BUILDING_TYPE) {
823  if (B_GetNumberOfBuildingsInBaseByBuildingType(base, buildingType) <= 0) {
824  /* there is no more building of this type */
825  B_SetBuildingStatus(base, buildingType, false);
826  /* check if this has an impact on other buildings */
827  B_UpdateStatusBuilding(base, buildingType, false);
828  /* we may have changed status of several building: update all capacities */
830  } else {
831  /* there is still at least one other building of the same type in base: just update capacity */
832  const baseCapacities_t cap = B_GetCapacityFromBuildingType(buildingType);
833  if (cap != MAX_CAP)
834  B_UpdateBaseCapacities(cap, base);
835  }
836  }
837 
838  /* call ondisable trigger only if building is not under construction
839  * (we do that after base capacity has been updated) */
840  if (runDisableCommand) {
841  if (B_FireEvent(buildingTemplate, base, B_ONDISABLE))
842  cgi->Com_DPrintf(DEBUG_CLIENT, "B_BuildingDestroy: %s %i %i;\n", buildingTemplate->onDisable, base->idx, buildingType);
843  }
844  if (B_FireEvent(buildingTemplate, base, B_ONDESTROY))
845  cgi->Com_DPrintf(DEBUG_CLIENT, "B_BuildingDestroy: %s %i %i;\n", buildingTemplate->onDestroy, base->idx, buildingType);
846 
848 
849  cgi->Cmd_ExecuteString("base_init %d", base->idx);
850 
851  return true;
852 }
853 
861 {
862  AIR_ForeachFromBase(aircraft, base) {
863  if (!AIR_IsAircraftOnGeoscape(aircraft))
864  continue;
865  base_t* newbase = nullptr;
866  bool moved = false;
867  while ((newbase = B_GetNext(newbase)) != nullptr) {
868  /* found a new homebase? */
869  if (base != newbase && !AIR_CheckMoveIntoNewHomebase(aircraft, newbase)) {
870  AIR_MoveAircraftIntoNewHomebase(aircraft, newbase);
871  moved = true;
872  break;
873  }
874  }
875 
876  if (moved)
877  continue;
878 
879  /* No base can hold this aircraft */
881  if (!MapIsWater(GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr))) {
882  CP_SpawnRescueMission(aircraft, nullptr);
883  } else {
884  /* Destroy the aircraft and everything onboard - the aircraft pointer
885  * is no longer valid after this point */
887  AIR_DestroyAircraft(aircraft);
888  }
889  }
890 }
891 
897 void B_Delete (base_t* base)
898 {
899  assert(base);
900 
901  if (base->alienContainment != nullptr) {
902  delete base->alienContainment;
903  base->alienContainment = nullptr;
904  }
905 }
906 
913 void B_Destroy (base_t* base)
914 {
915  assert(base);
916  base->baseStatus = BASE_DESTROYED;
917 
920 
921  /* do a reverse loop as buildings are going to be destroyed */
922  for (int buildingIdx = ccs.numBuildings[base->idx] - 1; buildingIdx >= 0; buildingIdx--) {
923  building_t* building = B_GetBuildingByIDX(base->idx, buildingIdx);
924  B_BuildingDestroy(building);
925  }
926 
927  E_DeleteAllEmployees(base);
928 
929  AIR_ForeachFromBase(aircraft, base) {
930  AIR_DeleteAircraft(aircraft);
931  }
932 
933  OBJZERO(base->storage);
934  CAP_SetCurrent(base, CAP_ITEMS, 0);
935 
936  base->alienInterest = 0;
937 
941  B_Delete(base);
942 }
943 
944 #ifdef DEBUG
945 
948 static void B_Destroy_f (void)
949 {
950  if (cgi->Cmd_Argc() < 2) {
951  cgi->Com_Printf("Usage: %s <baseIdx>\n", cgi->Cmd_Argv(0));
952  return;
953  }
954 
955  const int baseIdx = atoi(cgi->Cmd_Argv(1));
956  if (baseIdx < 0 || baseIdx >= MAX_BASES) {
957  cgi->Com_Printf("B_Destroy_f: baseIdx %i is outside bounds\n", baseIdx);
958  return;
959  }
960 
961  base_t* base = B_GetFoundedBaseByIDX(baseIdx);
962  if (!base) {
963  cgi->Com_Printf("B_Destroy_f: Base %i not founded\n", baseIdx);
964  return;
965  }
966 
967  B_Destroy(base);
968 }
969 #endif
970 
979 {
980  building->buildingStatus = status;
981 
982  /* we update the status of the building (we'll call this building building 1) */
983  const bool test = B_CheckUpdateBuilding(building);
984  if (test) {
985  base_t* base = building->base;
986  B_FireEvent(building, base, B_ONENABLE);
987  /* now, the status of this building may have changed the status of other building.
988  * We check that, but only for buildings which needed building 1 */
989  B_UpdateStatusBuilding(base, building->buildingType, true);
990  /* we may have changed status of several building: update all capacities */
992  } else {
993  /* no other status than status of building 1 has been modified
994  * update only status of building 1 */
996  if (cap != MAX_CAP) {
997  base_t* base = building->base;
998  B_UpdateBaseCapacities(cap, base);
999  }
1000  }
1001 }
1002 
1010 static void B_AddBuildingToBasePos (base_t* base, const building_t* buildingTemplate, bool hire, const vec2_t pos)
1011 {
1012  /* new building in base (not a template) */
1013  building_t* buildingNew;
1014 
1015  buildingNew = B_BuildBuilding(base, buildingTemplate, (int)pos[0], (int)pos[1]);
1016  if (!buildingNew)
1017  return;
1018  buildingNew->timeStart.day = 0;
1019  buildingNew->timeStart.sec = 0;
1021  cgi->Com_DPrintf(DEBUG_CLIENT, "Base %i new building: %s at (%.0f:%.0f)\n",
1022  base->idx, buildingNew->id, buildingNew->pos[0], buildingNew->pos[1]);
1023 
1024  if (hire)
1025  E_HireForBuilding(base, buildingNew, -1);
1026 
1027  /* now call the onenable trigger */
1028  if (B_FireEvent(buildingNew, base, B_ONENABLE))
1029  cgi->Com_DPrintf(DEBUG_CLIENT, "B_AddBuildingToBasePos: %s %i;\n", buildingNew->onEnable, base->idx);
1030 }
1031 
1040 static void B_BuildFromTemplate (base_t* base, const char* templateName, bool hire)
1041 {
1042  const baseTemplate_t* baseTemplate = B_GetBaseTemplate(templateName);
1043  int freeSpace = BASE_SIZE * BASE_SIZE;
1044 
1045  assert(base);
1046 
1047  if (baseTemplate) {
1048  /* find each building in the template */
1049  for (int i = 0; i < baseTemplate->numBuildings; i++) {
1050  const baseBuildingTile_t* buildingTile = &baseTemplate->buildings[i];
1051 
1052  if (base->map[buildingTile->posY][buildingTile->posX].building)
1053  continue;
1054 
1055  vec2_t pos;
1056  Vector2Set(pos, buildingTile->posX, buildingTile->posY);
1057  B_AddBuildingToBasePos(base, buildingTile->building, hire, pos);
1058  freeSpace--;
1059  }
1060  }
1061 
1062  /* we need to set up the mandatory buildings */
1063  for (int i = 0; i < ccs.numBuildingTemplates; i++) {
1064  building_t* building = &ccs.buildingTemplates[i];
1065  vec2_t pos;
1066 
1067  if (!building->mandatory || B_GetBuildingStatus(base, building->buildingType))
1068  continue;
1069 
1070  while (freeSpace > 0 && !B_GetBuildingStatus(base, building->buildingType)) {
1071  const int x = rand() % BASE_SIZE;
1072  const int y = rand() % BASE_SIZE;
1073  Vector2Set(pos, x, y);
1074  if (base->map[y][x].building)
1075  continue;
1076  B_AddBuildingToBasePos(base, building, hire, pos);
1077  freeSpace--;
1078  }
1081  if (!B_GetBuildingStatus(base, building->buildingType))
1082  cgi->Com_Error(ERR_DROP, "B_BuildFromTemplate: Cannot build base. No space for it's buildings!");
1083  }
1084 
1085  /* set building tile positions */
1086  for (int i = 0; i < BASE_SIZE; i++) {
1087  for (int j = 0; j < BASE_SIZE; j++) {
1088  base->map[i][j].posY = i;
1089  base->map[i][j].posX = j;
1090  }
1091  }
1092 
1093  /* Create random blocked fields in the base.
1094  * The first base never has blocked fields so we skip it. */
1095  if (ccs.campaignStats.basesBuilt >= 1) {
1096  const int j = round((frand() * (MAX_BLOCKEDFIELDS - MIN_BLOCKEDFIELDS)) + MIN_BLOCKEDFIELDS);
1097  B_AddBlockedTiles(base, j);
1098  }
1099 }
1100 
1107 void B_SetUpFirstBase (const campaign_t* campaign, base_t* base)
1108 {
1109  const equipDef_t* ed;
1110 
1111  /* Find the initial equipment definition for current campaign. */
1112  ed = cgi->INV_GetEquipmentDefinitionByID(campaign->equipment);
1113  /* Copy it to base storage. */
1114  base->storage = *ed;
1115 
1116  /* Add aircraft to the first base */
1117  LIST_Foreach(campaign->initialCraft, const char, aircraftName) {
1118  const aircraft_t* tempAircraft = AIR_GetAircraft(aircraftName);
1119  const baseCapacities_t craftCap = AIR_GetHangarCapacityType(tempAircraft);
1120 
1121  if (craftCap == MAX_CAP || CAP_GetFreeCapacity(base, craftCap) < 1) {
1122  cgi->Com_Printf("B_SetUpFirstBase: Cannot add %s aircraft to the base, no free space", tempAircraft->id);
1123  continue;
1124  }
1125 
1126  CP_UpdateCredits(ccs.credits - tempAircraft->price);
1127  aircraft_t* aircraft = AIR_NewAircraft(base, tempAircraft);
1128  /* refuel initial aicraft instantly */
1129  aircraft->fuel = aircraft->stats[AIR_STATS_FUELSIZE];
1130  if (!E_HireEmployeeByType(base, EMPL_PILOT))
1131  cgi->Com_Error(ERR_DROP, "B_SetUpFirstBase: Hiring pilot failed.");
1132 
1133  /* arm interceptor */
1134  if (craftCap == CAP_AIRCRAFT_SMALL) {
1135  AIM_AutoEquipAircraft(aircraft);
1136  continue;
1137  }
1138 
1139  /* assign team to the dropship */
1140  if (craftCap == CAP_AIRCRAFT_BIG) {
1141  const equipDef_t* equipDef = cgi->INV_GetEquipmentDefinitionByID(campaign->soldierEquipment);
1142  AIR_AssignInitial(aircraft);
1143  B_InitialEquipment(aircraft, equipDef);
1144  continue;
1145  }
1146  }
1147 }
1148 
1154 {
1155  int limit = 0;
1156  base_t* base = nullptr;
1157 
1158  /* count working Command Centers */
1159  while ((base = B_GetNext(base)) != nullptr) {
1160  if (B_GetBuildingStatus(base, B_COMMAND))
1161  limit++;
1162  }
1163 
1164  return limit * MAX_INSTALLATIONS_PER_BASE;
1165 }
1166 
1173 void B_SetName (base_t* base, const char* name)
1174 {
1175  Q_strncpyz(base->name, name, sizeof(base->name));
1176 }
1177 
1185 base_t* B_Build (const campaign_t* campaign, const vec2_t pos, const char* name, bool fillBase)
1186 {
1187  if (!campaign)
1188  cgi->Com_Error(ERR_DROP, "You can only build a base in an active campaign");
1189 
1190  base_t* base = B_GetFirstUnfoundedBase();
1191  if (!base)
1192  cgi->Com_Error(ERR_DROP, "Cannot build more bases");
1193 
1194  B_SetName(base, name);
1195  Vector2Copy(pos, base->pos);
1196 
1197  base->idx = ccs.campaignStats.basesBuilt;
1198  base->founded = true;
1199 
1200  /* increase this early because a lot of functions rely on this
1201  * value to get the base we are setting up here */
1202  ccs.numBases++;
1203 
1204  /* reset capacities */
1205  for (int i = 0; i < MAX_CAP; i++) {
1206  const baseCapacities_t cap = (baseCapacities_t)i;
1207  CAP_SetCurrent(base, cap, 0);
1208  }
1209 
1210  /* setup for first base */
1211  if (ccs.campaignStats.basesBuilt == 0 || fillBase) {
1212  if (campaign->firstBaseTemplate[0] == '\0')
1213  cgi->Com_Error(ERR_DROP, "No base template for setting up the first base given");
1214  B_BuildFromTemplate(base, campaign->firstBaseTemplate, true);
1215  } else {
1216  B_BuildFromTemplate(base, nullptr, true);
1217  }
1218  base->baseStatus = BASE_WORKING;
1219 
1220  /* a new base is not discovered (yet) */
1222 
1224 
1225  /* Reset Radar range */
1226  const float level = B_GetMaxBuildingLevel(base, B_RADAR);
1228  RADAR_InitialiseUFOs(&base->radar);
1229 
1230  B_ResetAllStatusAndCapacities(base, true);
1231 
1232  PR_UpdateProductionCap(base);
1233 
1235 
1237 
1238  return base;
1239 }
1240 
1246 const baseTemplate_t* B_GetBaseTemplate (const char* baseTemplateID)
1247 {
1248  if (!baseTemplateID)
1249  return nullptr;
1250 
1251  for (int i = 0; i < ccs.numBaseTemplates; i++)
1252  if (Q_streq(ccs.baseTemplates[i].id, baseTemplateID))
1253  return &ccs.baseTemplates[i];
1254 
1255  cgi->Com_Printf("Base Template %s not found\n", baseTemplateID);
1256  return nullptr;
1257 }
1258 
1263 bool B_MapIsCellFree (const base_t* base, int col, int row)
1264 {
1265  return col >= 0 && col < BASE_SIZE
1266  && row >= 0 && row < BASE_SIZE
1267  && B_GetBuildingAt(base, col, row) == nullptr
1268  && !B_IsTileBlocked(base, col, row);
1269 }
1270 
1279 building_t* B_BuildBuilding (base_t* base, const building_t* buildingTemplate, int col, int row)
1280 {
1281  if (!base)
1282  cgi->Com_Error(ERR_DROP, "no current base\n");
1283  if (!buildingTemplate)
1284  cgi->Com_Error(ERR_DROP, "no current building\n");
1285 
1286  if (row < 0 || row >= BASE_SIZE || col < 0 || col >= BASE_SIZE)
1287  return nullptr;
1288  if (col + buildingTemplate->size[0] > BASE_SIZE)
1289  return nullptr;
1290  if (row + buildingTemplate->size[1] > BASE_SIZE)
1291  return nullptr;
1292  if (!CP_CheckCredits(buildingTemplate->fixCosts))
1293  return nullptr;
1294 
1295  for (int y = row; y < row + buildingTemplate->size[1]; y++)
1296  for (int x = col; x < col + buildingTemplate->size[0]; x++)
1297  if (B_GetBuildingAt(base, x, y) != nullptr || B_IsTileBlocked(base, x, y))
1298  return nullptr;
1299 
1300  /* new building in base (not a template) */
1301  building_t* buildingNew = B_GetBuildingByIDX(base->idx, ccs.numBuildings[base->idx]);
1302  /* copy building from template list to base-buildings-list */
1303  *buildingNew = *buildingTemplate;
1304  /* self-link to building-list in base */
1305  buildingNew->idx = B_GetBuildingIDX(base, buildingNew);
1306  buildingNew->base = base;
1307  buildingNew->pos[0] = col;
1308  buildingNew->pos[1] = row;
1309 
1310  /* Refuse adding if it hurts coherency */
1311  if (base->baseStatus == BASE_WORKING) {
1312  linkedList_t* neighbours;
1313  bool coherent = false;
1314 
1315  neighbours = B_GetNeighbours(buildingNew);
1316  LIST_Foreach(neighbours, building_t, bldg) {
1317  if (B_IsBuildingBuiltUp(bldg)) {
1318  coherent = true;
1319  break;
1320  }
1321  }
1322  cgi->LIST_Delete(&neighbours);
1323 
1324  if (!coherent)
1325  return nullptr;
1326  }
1327 
1328  /* set building position */
1329  for (int y = row; y < row + buildingNew->size[1]; y++)
1330  for (int x = col; x < col + buildingNew->size[0]; x++)
1331  base->map[y][x].building = buildingNew;
1332 
1333 
1334  /* status and build (start) time */
1336  buildingNew->timeStart = ccs.date;
1337 
1338  CP_UpdateCredits(ccs.credits - buildingNew->fixCosts);
1339  ccs.numBuildings[base->idx]++;
1340 
1341  cgi->Cmd_ExecuteString("base_init %d", base->idx);
1342  B_FireEvent(buildingNew, base, B_ONCONSTRUCT);
1343 
1344  return buildingNew;
1345 }
1346 
1354 {
1355  if (!base) {
1356  cgi->Com_Printf("B_GetNumberOfBuildingsInBaseByTemplate: No base given!\n");
1357  return -1;
1358  }
1359 
1360  if (!tpl) {
1361  cgi->Com_Printf("B_GetNumberOfBuildingsInBaseByTemplate: no building-type given!\n");
1362  return -1;
1363  }
1364 
1365  /* Check if the template really is one. */
1366  if (tpl != tpl->tpl) {
1367  cgi->Com_Printf("B_GetNumberOfBuildingsInBaseByTemplate: No building-type given as parameter. It's probably a normal building!\n");
1368  return -1;
1369  }
1370 
1371  int numberOfBuildings = 0;
1372  building_t* building = nullptr;
1373  while ((building = B_GetNextBuilding(base, building))) {
1374  if (building->tpl == tpl && building->buildingStatus != B_STATUS_NOT_SET)
1375  numberOfBuildings++;
1376  }
1377  return numberOfBuildings;
1378 }
1379 
1387 {
1388  if (!base) {
1389  cgi->Com_Printf("B_GetNumberOfBuildingsInBaseByBuildingType: No base given!\n");
1390  return -1;
1391  }
1392 
1393  if (buildingType >= MAX_BUILDING_TYPE) {
1394  cgi->Com_Printf("B_GetNumberOfBuildingsInBaseByBuildingType: no sane building-type given!\n");
1395  return -1;
1396  }
1397 
1398  int numberOfBuildings = 0;
1399  building_t* building = nullptr;
1400  while ((building = B_GetNextBuildingByType(base, building, buildingType)))
1401  if (building->buildingStatus != B_STATUS_NOT_SET)
1402  numberOfBuildings++;
1403 
1404  return numberOfBuildings;
1405 }
1406 
1414 const building_t* B_GetBuildingInBaseByType (const base_t* base, buildingType_t buildingType, bool onlyWorking)
1415 {
1416  /* we maybe only want to get the working building (e.g. it might the
1417  * case that we don't have a powerplant and thus the searched building
1418  * is not functional) */
1419  if (onlyWorking && !B_GetBuildingStatus(base, buildingType))
1420  return nullptr;
1421 
1422  building_t* building = nullptr;
1423  while ((building = B_GetNextBuildingByType(base, building, buildingType)))
1424  return building;
1425 
1426  return nullptr;
1427 }
1428 
1435 void B_ParseBaseTemplate (const char* name, const char** text)
1436 {
1437  const char* errhead = "B_ParseBaseTemplate: unexpected end of file (names ";
1438  const char* token;
1439  baseTemplate_t* baseTemplate;
1440  vec2_t pos;
1441  bool map[BASE_SIZE][BASE_SIZE];
1442  byte buildingNums[MAX_BUILDINGS];
1443 
1444  /* get token */
1445  token = Com_Parse(text);
1446 
1447  if (!*text || *token != '{') {
1448  cgi->Com_Printf("B_ParseBaseTemplate: Template \"%s\" without body ignored\n", name);
1449  return;
1450  }
1451 
1453  cgi->Com_Error(ERR_DROP, "B_ParseBaseTemplate: too many base templates");
1454 
1455  /* create new Template */
1456  baseTemplate = &ccs.baseTemplates[ccs.numBaseTemplates];
1457  baseTemplate->id = cgi->PoolStrDup(name, cp_campaignPool, 0);
1458 
1459  /* clear map for checking duplicate positions and buildingNums for checking moreThanOne constraint */
1460  OBJZERO(map);
1461  OBJZERO(buildingNums);
1462 
1464 
1465  do {
1466  /* get the building */
1467  token = cgi->Com_EParse(text, errhead, baseTemplate->id);
1468  if (!*text)
1469  break;
1470  if (*token == '}')
1471  break;
1472 
1473  if (!Q_streq(token, "building")) {
1474  cgi->Com_Error(ERR_DROP, "B_ParseBaseTemplate: \"building\" token expected but \"%s\" found", token);
1475  }
1476 
1477  linkedList_t* list;
1478  if (!cgi->Com_ParseList(text, &list)) {
1479  cgi->Com_Error(ERR_DROP, "B_ParseBaseTemplate: error while reading building tuple");
1480  }
1481 
1482  if (cgi->LIST_Count(list) != 2) {
1483  cgi->Com_Error(ERR_DROP, "B_ParseBaseTemplate: building tuple must contains 2 elements (id pos)");
1484  }
1485 
1486  const char* buildingToken = (char*)list->data;
1487  const char* positionToken = (char*)list->next->data;
1488 
1489  if (baseTemplate->numBuildings >= MAX_BASEBUILDINGS)
1490  cgi->Com_Error(ERR_DROP, "B_ParseBaseTemplate: too many buildings");
1491 
1492  /* check if building type is known */
1493  baseBuildingTile_t* tile = &baseTemplate->buildings[baseTemplate->numBuildings];
1494  baseTemplate->numBuildings++;
1495 
1496  for (int i = 0; i < ccs.numBuildingTemplates; i++)
1497  if (Q_streq(ccs.buildingTemplates[i].id, buildingToken)) {
1498  tile->building = &ccs.buildingTemplates[i];
1499  if (tile->building->maxCount >= 0 && tile->building->maxCount <= buildingNums[i])
1500  cgi->Com_Error(ERR_DROP, "B_ParseBaseTemplate: Found more %s than allowed in template %s (%d))", buildingToken, baseTemplate->id, tile->building->maxCount);
1501  buildingNums[i]++;
1502  break;
1503  }
1504 
1505  if (!tile->building)
1506  cgi->Com_Error(ERR_DROP, "B_ParseBaseTemplate: Could not find building with id %s\n", baseTemplate->id);
1507 
1508  /* get the position */
1509  cgi->Com_EParseValue(pos, positionToken, V_POS, 0, sizeof(vec2_t));
1510  tile->posX = pos[0];
1511  tile->posY = pos[1];
1512  if (tile->posX < 0 || tile->posX >= BASE_SIZE || tile->posY < 0 || tile->posY >= BASE_SIZE)
1513  cgi->Com_Error(ERR_DROP, "Invalid template coordinates for building %s in template %s given",
1514  tile->building->id, baseTemplate->id);
1515 
1516  /* check for buildings on same position */
1517  if (map[tile->posY][tile->posX])
1518  cgi->Com_Error(ERR_DROP, "Base template '%s' has ambiguous positions for buildings set.", baseTemplate->id);
1519  map[tile->posY][tile->posX] = true;
1520 
1521  cgi->LIST_Delete(&list);
1522  } while (*text);
1523 
1524  /* templates without the must-have buildings can't be used */
1525  for (int i = 0; i < ccs.numBuildingTemplates; i++) {
1526  const building_t* building = &ccs.buildingTemplates[i];
1527  if (building && building->mandatory && !buildingNums[i]) {
1528  cgi->Com_Error(ERR_DROP, "Every base template needs one '%s'! '%s' has none.", building->id, baseTemplate->id);
1529  }
1530  }
1531 }
1532 
1538 {
1539  for (int baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
1540  base_t* base = B_GetBaseByIDX(baseIdx);
1541  if (!base->founded)
1542  return base;
1543  }
1544 
1545  return nullptr;
1546 }
1547 
1554 {
1555  base_t* b = nullptr;
1556  while ((b = B_GetNext(b)) != nullptr) {
1557  if (b == base) {
1558  b->selected = true;
1559  if (b->aircraftCurrent == nullptr)
1561  } else
1562  b->selected = false;
1563  }
1564 
1565  if (base) {
1567  cgi->Cvar_Set("mn_base_title", "%s", base->name);
1568  cgi->Cvar_SetValue("mn_base_status_id", base->baseStatus);
1569  } else {
1570  cgi->Cvar_Set("mn_base_title", "");
1571  cgi->Cvar_Set("mn_base_status_id", "");
1572  }
1573 }
1574 
1579 {
1580  base_t* base = nullptr;
1581  while ((base = B_GetNext(base)) != nullptr)
1582  if (base->selected)
1583  return base;
1584 
1585  return nullptr;
1586 }
1587 
1592 void B_SelectBase (const base_t* base)
1593 {
1594  /* set up a new base */
1595  if (!base) {
1596  /* if player hit the "create base" button while creating base mode is enabled
1597  * that means that player wants to quit this mode */
1598  if (ccs.mapAction == MA_NEWBASE) {
1599  GEO_ResetAction();
1600  return;
1601  }
1602 
1603  if (B_GetCount() < MAX_BASES) {
1604  /* show radar overlay (if not already displayed) */
1606  GEO_SetOverlay("radar", 1);
1608  } else {
1609  ccs.mapAction = MA_NONE;
1610  }
1611  } else {
1612  cgi->Com_DPrintf(DEBUG_CLIENT, "B_SelectBase_f: select base with id %i\n", base->idx);
1613  ccs.mapAction = MA_NONE;
1614  cgi->Cmd_ExecuteString(va("ui_push bases %d", base->idx));
1616  }
1617 }
1618 
1622 static void CL_SwapSkill (character_t* cp1, character_t* cp2, abilityskills_t skill)
1623 {
1624  int tmp1, tmp2;
1625  tmp1 = cp1->score.skills[skill];
1626  tmp2 = cp2->score.skills[skill];
1627  cp1->score.skills[skill] = tmp2;
1628  cp2->score.skills[skill] = tmp1;
1629 
1630  tmp1 = cp1->score.initialSkills[skill];
1631  tmp2 = cp2->score.initialSkills[skill];
1632  cp1->score.initialSkills[skill] = tmp2;
1633  cp2->score.initialSkills[skill] = tmp1;
1634 
1635  tmp1 = cp1->score.experience[skill];
1636  tmp2 = cp2->score.experience[skill];
1637  cp1->score.experience[skill] = tmp2;
1638  cp2->score.experience[skill] = tmp1;
1639 }
1640 
1641 static void CL_DoSwapSkills (character_t* cp1, character_t* cp2, const abilityskills_t skill)
1642 {
1643  if (cp1->score.skills[skill] < cp2->score.skills[skill])
1644  CL_SwapSkill(cp1, cp2, skill);
1645 
1646  switch (skill) {
1647  case SKILL_CLOSE:
1648  if (cp1->score.skills[ABILITY_SPEED] < cp2->score.skills[ABILITY_SPEED])
1649  CL_SwapSkill(cp1, cp2, ABILITY_SPEED);
1650  break;
1651 #if 0
1652  case SKILL_HEAVY:
1653  if (cp1->score.skills[ABILITY_POWER] < cp2->score.skills[ABILITY_POWER])
1654  CL_SwapSkill(cp1, cp2, ABILITY_POWER);
1655  break;
1656 #endif
1657  case SKILL_ASSAULT:
1658  /* no related basic attribute */
1659  break;
1660  case SKILL_SNIPER:
1662  CL_SwapSkill(cp1, cp2, ABILITY_ACCURACY);
1663  break;
1664  case SKILL_EXPLOSIVE:
1665  if (cp1->score.skills[ABILITY_MIND] < cp2->score.skills[ABILITY_MIND])
1666  CL_SwapSkill(cp1, cp2, ABILITY_MIND);
1667  break;
1668  default:
1669  cgi->Com_Error(ERR_DROP, "CL_SwapSkills: illegal skill %i.\n", skill);
1670  }
1671 }
1672 
1682 static int CL_GetSkillIndicator (const character_t* chr, abilityskills_t skill)
1683 {
1684  const fireDef_t* fdRight = nullptr;
1685  const fireDef_t* fdHolster = nullptr;
1686  const Item* rightHand = chr->inv.getRightHandContainer();
1687  const Item* holster = chr->inv.getHolsterContainer();
1688 
1689  if (rightHand && rightHand->ammoDef() && rightHand->def())
1690  fdRight = rightHand->getFiredefs();
1691  if (holster && holster->ammoDef() && holster->def())
1692  fdHolster = holster->getFiredefs();
1693  /* disregard left hand, or dual-wielding guys are too good */
1694 
1695  if (fdHolster == nullptr)
1696  return -1;
1697  if (fdRight == nullptr)
1698  return -1;
1699 
1700  const byte fmode1 = 0;
1701  const byte fmode2 = 1;
1702  int no = 0;
1703  if (rightHand != nullptr) {
1704  const fireDef_t* fd = rightHand->ammoDef()->fd[fdRight->weapFdsIdx];
1705  no += 2 * (skill == fd[fmode1].weaponSkill);
1706  no += 2 * (skill == fd[fmode2].weaponSkill);
1707  }
1708  if (holster != nullptr && holster->isReloadable()) {
1709  const fireDef_t* fd = holster->ammoDef()->fd[fdHolster->weapFdsIdx];
1710  no += (skill == fd[fmode1].weaponSkill);
1711  no += (skill == fd[fmode2].weaponSkill);
1712  }
1713  return no;
1714 }
1715 
1719 static void CL_SwapSkills (linkedList_t* team)
1720 {
1721  const int teamSize = cgi->LIST_Count(team);
1722 
1723  for (int i = 0; i < teamSize; i++) {
1724  /* running the loops below is not enough, we need transitive closure */
1725  /* I guess num times is enough --- could anybody prove this? */
1726  /* or perhaps 2 times is enough as long as weapons have 1 skill? */
1727  for (int x = ABILITY_NUM_TYPES; x < SKILL_NUM_TYPES; x++) {
1728  const abilityskills_t skill = (abilityskills_t)x;
1729  LIST_Foreach(team, character_t, cp1) {
1730  if (cp1__iter == nullptr)
1731  continue;
1732 
1733  const int no1 = CL_GetSkillIndicator(cp1, skill);
1734  if (no1 == -1)
1735  continue;
1736 
1737  LIST_Foreach(cp1__iter->next, character_t, cp2) {
1738  const int no2 = CL_GetSkillIndicator(cp2, skill);
1739  if (no2 == -1)
1740  continue;
1741 
1742  if (no1 > no2 /* more use of this skill */
1743  || (no1 && no1 == no2)) { /* or earlier on list */
1744  CL_DoSwapSkills(cp1, cp2, skill);
1745  } else if (no1 < no2) {
1746  CL_DoSwapSkills(cp2, cp1, skill);
1747  }
1748  }
1749  }
1750  }
1751  }
1752 }
1753 
1759 static void B_InitialEquipment (aircraft_t* aircraft, const equipDef_t* ed)
1760 {
1761  base_t* homebase;
1762  linkedList_t* chrListTemp = nullptr;
1763 
1764  assert(aircraft);
1765  homebase = aircraft->homebase;
1766  assert(homebase);
1767  assert(ed);
1768 
1769  LIST_Foreach(aircraft->acTeam, Employee, employee) {
1770  character_t* chr = &employee->chr;
1771 
1772  /* pack equipment */
1773  cgi->Com_DPrintf(DEBUG_CLIENT, "B_InitialEquipment: Packing initial equipment for %s.\n", chr->name);
1774  cgi->INV_EquipActor(chr, ed, nullptr, cgi->GAME_GetChrMaxLoad(chr));
1775  cgi->LIST_AddPointer(&chrListTemp, (void*)chr);
1776  }
1777 
1778  AIR_MoveEmployeeInventoryIntoStorage(*aircraft, homebase->storage);
1779  CL_SwapSkills(chrListTemp);
1780  cgi->LIST_Delete(&chrListTemp);
1781  CAP_UpdateStorageCap(homebase);
1782 }
1783 
1789 void B_BaseResetStatus (base_t* const base)
1790 {
1791  assert(base);
1792  base->baseStatus = BASE_NOT_USED;
1793 }
1794 
1795 #ifdef DEBUG
1796 
1801 static void B_BuildingList_f (void)
1802 {
1803  base_t* base = nullptr;
1804  while ((base = B_GetNext(base)) != nullptr) {
1805  cgi->Com_Printf("\nBase id %i: %s\n", base->idx, base->name);
1807  for (int j = 0; j < ccs.numBuildings[base->idx]; j++) {
1808  const building_t* building = B_GetBuildingByIDX(base->idx, j);
1809 
1810  cgi->Com_Printf("...Building: %s #%i - id: %i\n", building->id,
1811  B_GetNumberOfBuildingsInBaseByTemplate(base, building->tpl), base->idx);
1812  cgi->Com_Printf("...image: %s\n", building->image);
1813  cgi->Com_Printf(".....Status:\n");
1814 
1815  for (int k = 0; k < BASE_SIZE * BASE_SIZE; k++) {
1816  if (k > 1 && k % BASE_SIZE == 0)
1817  cgi->Com_Printf("\n");
1818  cgi->Com_Printf("%i ", building->buildingStatus);
1819  if (!building->buildingStatus)
1820  break;
1821  }
1822  cgi->Com_Printf("\n");
1823  }
1824  }
1825 }
1826 
1832 static void B_BaseList_f (void)
1833 {
1834  base_t* base = nullptr;
1835  while ((base = B_GetNext(base)) != nullptr) {
1836  if (!base->founded) {
1837  cgi->Com_Printf("Base idx %i not founded\n\n", base->idx);
1838  continue;
1839  }
1840 
1841  cgi->Com_Printf("Base idx %i\n", base->idx);
1842  cgi->Com_Printf("Base name %s\n", base->name);
1843  cgi->Com_Printf("Base founded %i\n", base->founded);
1844  cgi->Com_Printf("Base numMissileBattery %i\n", base->numBatteries);
1845  cgi->Com_Printf("Base numLaserBattery %i\n", base->numLasers);
1846  cgi->Com_Printf("Base radarRange %i\n", base->radar.range);
1847  cgi->Com_Printf("Base trackingRange %i\n", base->radar.trackingRange);
1848  cgi->Com_Printf("Base numSensoredAircraft %i\n", base->radar.numUFOs);
1849  cgi->Com_Printf("Base Alien interest %f\n", base->alienInterest);
1850  cgi->Com_Printf("Base hasBuilding[]:\n");
1851  cgi->Com_Printf("Misc Lab Quar Stor Work Hosp Hang Cont SHgr UHgr SUHg Powr cgi->Cmd AMtr Entr Miss Lasr Rdr Team\n");
1852  for (int j = 0; j < MAX_BUILDING_TYPE; j++) {
1853  const buildingType_t type = (buildingType_t)j;
1854  cgi->Com_Printf(" %i ", B_GetBuildingStatus(base, type));
1855  }
1856  cgi->Com_Printf("\n");
1857  cgi->Com_Printf("Base pos %.02f:%.02f\n", base->pos[0], base->pos[1]);
1858  cgi->Com_Printf("Base map:\n");
1859  for (int row = 0; row < BASE_SIZE; row++) {
1860  if (row > 0)
1861  cgi->Com_Printf("\n");
1862  for (int col = 0; col < BASE_SIZE; col++)
1863  cgi->Com_Printf("%2i (%3i: %3i) ", (base->map[row][col].building ? base->map[row][col].building->idx : -1),
1864  base->map[row][col].posX, base->map[row][col].posY);
1865  }
1866  cgi->Com_Printf("\n\n");
1867  }
1868 }
1869 
1874 static void B_PrintCapacities_f (void)
1875 {
1876  if (cgi->Cmd_Argc() < 2) {
1877  cgi->Com_Printf("Usage: %s <baseID>\n", cgi->Cmd_Argv(0));
1878  return;
1879  }
1880 
1881  const int baseIdx = atoi(cgi->Cmd_Argv(1));
1882  if (baseIdx >= B_GetCount()) {
1883  cgi->Com_Printf("invalid baseID (%s)\n", cgi->Cmd_Argv(1));
1884  return;
1885  }
1886  base_t* base = B_GetBaseByIDX(baseIdx);
1887  for (int i = 0; i < MAX_CAP; i++) {
1888  const baseCapacities_t cap = (baseCapacities_t)i;
1889  const buildingType_t buildingType = B_GetBuildingTypeByCapacity(cap);
1890  if (buildingType >= MAX_BUILDING_TYPE) {
1891  cgi->Com_Printf("B_PrintCapacities_f: Could not find building associated with capacity %i\n", i);
1892  continue;
1893  }
1894  for (int j = 0; j < ccs.numBuildingTemplates; j++) {
1895  if (ccs.buildingTemplates[j].buildingType != buildingType)
1896  continue;
1897 
1898  cgi->Com_Printf("Building: %s, capacity max: %i, capacity cur: %i\n",
1899  ccs.buildingTemplates[j].id, CAP_GetMax(base, i), CAP_GetCurrent(base, cap));
1900  break;
1901  }
1902  }
1903 }
1904 
1908 static void B_BuildingConstructionFinished_f (void)
1909 {
1910  if (cgi->Cmd_Argc() < 2) {
1911  cgi->Com_Printf("Usage: %s <baseIDX>\n", cgi->Cmd_Argv(0));
1912  return;
1913  }
1914  base_t* base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(1)));
1915  if (!base) {
1916  cgi->Com_Printf("Invalid base idx: %s\n", cgi->Cmd_Argv(1));
1917  return;
1918  }
1919 
1920  for (int i = 0; i < ccs.numBuildings[base->idx]; i++) {
1921  building_t* building = B_GetBuildingByIDX(base->idx, i);
1923  continue;
1924 
1926  building->timeStart.day = 0;
1927  building->timeStart.sec = 0;
1928  B_FireEvent(building, base, B_ONENABLE);
1929  }
1930  /* update menu */
1931  B_SelectBase(base);
1932 }
1933 
1938 static void B_ResetAllStatusAndCapacities_f (void)
1939 {
1940  base_t* base;
1941 
1942  base = nullptr;
1943  while ((base = B_GetNext(base)) != nullptr) {
1944  /* set buildingStatus[] and capacities.max values */
1945  B_ResetAllStatusAndCapacities(base, false);
1946  }
1947 }
1948 
1952 static void B_CheckCoherency_f (void)
1953 {
1954  if (cgi->Cmd_Argc() < 2) {
1955  cgi->Com_Printf("Usage: %s <baseIDX>\n", cgi->Cmd_Argv(0));
1956  return;
1957  }
1958  base_t* base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(1)));
1959  if (!base) {
1960  cgi->Com_Printf("Invalid base idx: %s\n", cgi->Cmd_Argv(1));
1961  return;
1962  }
1963 
1964  cgi->Com_Printf("Base '%s' (idx:%i) is %scoherent.\n", base->name, base->idx, (B_IsCoherent(base)) ? "" : "not ");
1965 }
1966 #endif
1967 
1971 void B_InitStartup (void)
1972 {
1973 #ifdef DEBUG
1974  cgi->Cmd_AddCommand("debug_listbase", B_BaseList_f, "Print base information to the game console");
1975  cgi->Cmd_AddCommand("debug_listbuilding", B_BuildingList_f, "Print building information to the game console");
1976  cgi->Cmd_AddCommand("debug_listcapacities", B_PrintCapacities_f, "Debug function to show all capacities in given base");
1977  cgi->Cmd_AddCommand("debug_basereset", B_ResetAllStatusAndCapacities_f, "Reset building status and capacities of all bases");
1978  cgi->Cmd_AddCommand("debug_destroybase", B_Destroy_f, "Destroy a base");
1979  cgi->Cmd_AddCommand("debug_buildingfinished", B_BuildingConstructionFinished_f, "Finish construction for every building in the current base");
1980  cgi->Cmd_AddCommand("debug_baseiscoherent", B_CheckCoherency_f, "Checks if all buildings are connected on a base");
1981 #endif
1982 }
1983 
1989 {
1991  return false;
1992 
1993  if (!B_IsBuildingBuiltUp(building))
1994  return false;
1995 
1996  base_t* base = building->base;
1997 
1999  if (B_FireEvent(building, base, B_ONENABLE))
2000  cgi->Com_DPrintf(DEBUG_CLIENT, "B_CheckBuildingConstruction: %s %i;\n", building->onEnable, base->idx);
2001 
2002  return true;
2003 }
2004 
2011 {
2012  base_t* base = nullptr;
2013  while ((base = B_GetNext(base)) != nullptr) {
2014  building_t* building = nullptr;
2015  while ((building = B_GetNextBuilding(base, building))) {
2016  if (!B_CheckBuildingConstruction(building))
2017  continue;
2018 
2020  _("Construction of %s building finished in %s."), _(building->name), base->name);
2022  }
2023  }
2024 }
2025 
2031 static void B_SellOrAddItems (aircraft_t* aircraft)
2032 {
2033  int numitems = 0;
2034  int gained = 0;
2035 
2036  assert(aircraft);
2037  base_t* base = aircraft->homebase;
2038  assert(base);
2039 
2040  if (aircraft->itemCargo == nullptr)
2041  return;
2042 
2043  linkedList_t* items = aircraft->itemCargo->list();
2044  LIST_Foreach(items, itemCargo_t, item) {
2045  technology_t* const tech = RS_GetTechForItem(item->objDef);
2046 
2047  if (!RS_IsResearched_ptr(tech)) {
2048  /* Items not researched cannot be thrown out even if not enough space in storage. */
2049  B_AddToStorage(base, item->objDef, item->amount);
2050  if (item->amount > 0)
2051  RS_MarkCollected(tech);
2052  continue;
2053  } else {
2054  /* If the related technology is researched, check the autosell option. */
2055  if (ccs.eMarket.autosell[item->objDef->idx]) { /* Sell items if autosell is enabled. */
2056  BS_SellItem(item->objDef, nullptr, item->amount);
2057  gained += BS_GetItemSellingPrice(item->objDef) * item->amount;
2058  numitems += item->amount;
2059  } else {
2060  B_AddToStorage(base, item->objDef, item->amount);
2061  }
2062  continue;
2063  }
2064  }
2065  cgi->LIST_Delete(&items);
2066 
2067  if (numitems > 0) {
2068  Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("By selling %s you gathered %i credits."),
2069  va(ngettext("%i collected item", "%i collected items", numitems), numitems), gained);
2070  MS_AddNewMessage(_("Notice"), cp_messageBuffer);
2071  }
2072 
2073  aircraft->itemCargo->empty();
2074 
2075  /* Mark new technologies researchable. */
2076  RS_MarkResearchable(aircraft->homebase);
2077  /* Recalculate storage capacity, to fix wrong capacity if a soldier drops something on the ground */
2078  CAP_UpdateStorageCap(aircraft->homebase);
2079 }
2080 
2086 {
2087  /* Don't call cargo functions if aircraft is not a transporter. */
2088  if (aircraft->maxTeamSize <= 0)
2089  return;
2090 
2091  /* Add aliens to Alien Containment. */
2092  AL_AddAliens(aircraft);
2093  /* Sell collected items or add them to storage. */
2094  B_SellOrAddItems(aircraft);
2095 }
2096 
2104 {
2105  /* AA Missiles should miss */
2107  /* Reset UFO sensored on radar */
2108  RADAR_InitialiseUFOs(&aircraft->radar);
2109  /* Reload weapons */
2110  AII_ReloadAircraftWeapons(aircraft);
2111 
2112  B_DumpAircraftToHomeBase(aircraft);
2113 }
2114 
2120 bool B_BaseHasItem (const base_t* base, const objDef_t* item)
2121 {
2122  if (item->isVirtual)
2123  return true;
2124 
2125  return B_ItemInBase(item, base) > 0;
2126 }
2127 
2134 int B_ItemInBase (const objDef_t* item, const base_t* base)
2135 {
2136  const equipDef_t* ed;
2137 
2138  if (!item)
2139  return -1;
2140  if (item->isVirtual)
2141  return -1;
2142  if (!base)
2143  return -1;
2144 
2145  ed = &base->storage;
2146 
2147  if (!ed)
2148  return -1;
2149 
2150  return ed->numItems[item->idx];
2151 }
2152 
2162 {
2163  switch (cap) {
2164  case CAP_ALIENS:
2165  case CAP_EMPLOYEES:
2166  case CAP_LABSPACE:
2167  case CAP_WORKSPACE:
2168  case CAP_ITEMS:
2169  case CAP_AIRCRAFT_SMALL:
2170  case CAP_AIRCRAFT_BIG:
2171  case CAP_ANTIMATTER:
2172  {
2173  int buildingTemplateIDX = -1;
2174  const buildingType_t buildingType = B_GetBuildingTypeByCapacity(cap);
2175  int capacity = 0;
2176 
2177  /* Reset given capacity in current base. */
2178  CAP_SetMax(base, cap, 0);
2179  /* Get building capacity. */
2180  for (int i = 0; i < ccs.numBuildingTemplates; i++) {
2181  const building_t* b = &ccs.buildingTemplates[i];
2182  if (b->buildingType == buildingType) {
2183  capacity = b->capacity;
2184  cgi->Com_DPrintf(DEBUG_CLIENT, "Building: %s capacity: %i\n", b->id, capacity);
2185  buildingTemplateIDX = i;
2186  break;
2187  }
2188  }
2189  /* Finally update capacity. */
2190  building_t* building = nullptr;
2191  while ((building = B_GetNextBuildingByType(base, building, buildingType)))
2193  CAP_AddMax(base, cap, capacity);
2194 
2195  if (buildingTemplateIDX != -1)
2196  cgi->Com_DPrintf(DEBUG_CLIENT, "B_UpdateBaseCapacities: updated capacity of %s: %i\n",
2197  ccs.buildingTemplates[buildingTemplateIDX].id, CAP_GetMax(base, cap));
2198 
2199  if (cap == CAP_ALIENS) {
2200  const capacities_t* alienCap = CAP_Get(base, CAP_ALIENS);
2201  if (base->alienContainment != nullptr && alienCap->max == 0 && alienCap->cur == 0) {
2202  delete base->alienContainment;
2203  base->alienContainment = nullptr;
2204  } else if (base->alienContainment == nullptr && alienCap->max > 0) {
2205  base->alienContainment = new AlienContainment(CAP_Get(base, CAP_ALIENS), nullptr);
2206  }
2207  }
2208  break;
2209  }
2210  case MAX_CAP:
2211  cgi->Com_DPrintf(DEBUG_CLIENT, "B_UpdateBaseCapacities: going to update ALL capacities.\n");
2212  /* Loop through all capacities and update them. */
2213  for (int i = 0; i < cap; i++) {
2214  const baseCapacities_t cap = (baseCapacities_t) i;
2215  B_UpdateBaseCapacities(cap, base);
2216  }
2217  break;
2218  default:
2219  cgi->Com_Error(ERR_DROP, "Unknown capacity limit for this base: %i \n", cap);
2220  }
2221 }
2222 
2229 void B_SaveBaseSlotsXML (const baseWeapon_t* weapons, const int numWeapons, xmlNode_t* node)
2230 {
2231  for (int i = 0; i < numWeapons; i++) {
2232  xmlNode_t* sub = cgi->XML_AddNode(node, SAVE_BASES_WEAPON);
2233  AII_SaveOneSlotXML(sub, &weapons[i].slot, true);
2234  cgi->XML_AddBool(sub, SAVE_BASES_AUTOFIRE, weapons[i].autofire);
2235  if (weapons[i].target)
2236  cgi->XML_AddInt(sub, SAVE_BASES_TARGET, weapons[i].target->idx);
2237  }
2238 }
2239 
2245 bool B_SaveStorageXML (xmlNode_t* parent, const equipDef_t& equip)
2246 {
2247  for (int k = 0; k < cgi->csi->numODs; k++) {
2248  const objDef_t* od = INVSH_GetItemByIDX(k);
2249  if (equip.numItems[k] || equip.numItemsLoose[k]) {
2250  xmlNode_t* node = cgi->XML_AddNode(parent, SAVE_BASES_ITEM);
2251 
2252  cgi->XML_AddString(node, SAVE_BASES_ODS_ID, od->id);
2253  cgi->XML_AddIntValue(node, SAVE_BASES_NUM, equip.numItems[k]);
2254  cgi->XML_AddByteValue(node, SAVE_BASES_NUMLOOSE, equip.numItemsLoose[k]);
2255  }
2256  }
2257  return true;
2258 }
2259 
2264 bool B_SaveXML (xmlNode_t* parent)
2265 {
2266  xmlNode_t* bases;
2267  base_t* b;
2268 
2269  bases = cgi->XML_AddNode(parent, SAVE_BASES_BASES);
2270  b = nullptr;
2271  while ((b = B_GetNext(b)) != nullptr) {
2272 
2273  if (!b->founded) {
2274  cgi->Com_Printf("B_SaveXML: Base (idx: %i) not founded!\n", b->idx);
2275  return false;
2276  }
2277 
2278  cgi->Com_RegisterConstList(saveBaseConstants);
2279 
2280  xmlNode_t* act_base = cgi->XML_AddNode(bases, SAVE_BASES_BASE);
2281  cgi->XML_AddInt(act_base, SAVE_BASES_IDX, b->idx);
2282  cgi->XML_AddString(act_base, SAVE_BASES_NAME, b->name);
2283  cgi->XML_AddPos3(act_base, SAVE_BASES_POS, b->pos);
2285  cgi->XML_AddFloat(act_base, SAVE_BASES_ALIENINTEREST, b->alienInterest);
2286 
2287  /* building space */
2288  xmlNode_t* node = cgi->XML_AddNode(act_base, SAVE_BASES_BUILDINGSPACE);
2289  for (int row = 0; row < BASE_SIZE; row++) {
2290  for (int column = 0; column < BASE_SIZE; column++) {
2291  xmlNode_t* snode = cgi->XML_AddNode(node, SAVE_BASES_BUILDING);
2293  cgi->XML_AddInt(snode, SAVE_BASES_X, row);
2294  cgi->XML_AddInt(snode, SAVE_BASES_Y, column);
2295  if (B_GetBuildingAt(b, column, row))
2296  cgi->XML_AddInt(snode, SAVE_BASES_BUILDINGINDEX, B_GetBuildingAt(b, column, row)->idx);
2297  cgi->XML_AddBoolValue(snode, SAVE_BASES_BLOCKED, B_IsTileBlocked(b, column, row));
2298  }
2299  }
2300  /* buildings */
2301  node = cgi->XML_AddNode(act_base, SAVE_BASES_BUILDINGS);
2302  building_t* building = nullptr;
2303  while ((building = B_GetNextBuilding(b, building))) {
2304  xmlNode_t* snode;
2305 
2306  if (!building->tpl)
2307  continue;
2308 
2309  snode = cgi->XML_AddNode(node, SAVE_BASES_BUILDING);
2310  cgi->XML_AddString(snode, SAVE_BASES_BUILDINGTYPE, building->tpl->id);
2311  cgi->XML_AddInt(snode, SAVE_BASES_BUILDING_PLACE, building->idx);
2313  cgi->XML_AddDate(snode, SAVE_BASES_BUILDINGTIMESTART, building->timeStart.day, building->timeStart.sec);
2314  cgi->XML_AddInt(snode, SAVE_BASES_BUILDINGBUILDTIME, building->buildTime);
2315  cgi->XML_AddFloatValue(snode, SAVE_BASES_BUILDINGLEVEL, building->level);
2316  cgi->XML_AddPos2(snode, SAVE_BASES_POS, building->pos);
2317  }
2318  /* base defences */
2319  node = cgi->XML_AddNode(act_base, SAVE_BASES_BATTERIES);
2321  node = cgi->XML_AddNode(act_base, SAVE_BASES_LASERS);
2322  B_SaveBaseSlotsXML(b->lasers, b->numLasers, node);
2323  /* store equipment */
2324  node = cgi->XML_AddNode(act_base, SAVE_BASES_STORAGE);
2325  B_SaveStorageXML(node, b->storage);
2326  /* radar */
2327  cgi->XML_AddIntValue(act_base, SAVE_BASES_RADARRANGE, b->radar.range);
2328  cgi->XML_AddIntValue(act_base, SAVE_BASES_TRACKINGRANGE, b->radar.trackingRange);
2329  /* alien containment */
2330  if (b->alienContainment) {
2331  node = cgi->XML_AddNode(act_base, SAVE_BASES_ALIENCONTAINMENT);
2332  b->alienContainment->save(node);
2333  }
2334 
2335  cgi->Com_UnregisterConstList(saveBaseConstants);
2336  }
2337  return true;
2338 }
2339 
2348 int B_LoadBaseSlotsXML (baseWeapon_t* weapons, int max, xmlNode_t* p)
2349 {
2350  int i;
2351  xmlNode_t* s;
2352  for (i = 0, s = cgi->XML_GetNode(p, SAVE_BASES_WEAPON); s && i < max; i++, s = cgi->XML_GetNextNode(s, p, SAVE_BASES_WEAPON)) {
2353  const int target = cgi->XML_GetInt(s, SAVE_BASES_TARGET, -1);
2354  AII_LoadOneSlotXML(s, &weapons[i].slot, true);
2355  weapons[i].autofire = cgi->XML_GetBool(s, SAVE_BASES_AUTOFIRE, true);
2356  weapons[i].target = (target >= 0) ? UFO_GetByIDX(target) : nullptr;
2357  }
2358  return i;
2359 }
2360 
2365 static bool B_PostLoadInitCapacity (void)
2366 {
2367  base_t* base = nullptr;
2368  while ((base = B_GetNext(base)) != nullptr)
2369  B_ResetAllStatusAndCapacities(base, true);
2370 
2371  return true;
2372 }
2373 
2378 bool B_PostLoadInit (void)
2379 {
2380  return B_PostLoadInitCapacity();
2381 }
2382 
2388 bool B_LoadStorageXML (xmlNode_t* parent, equipDef_t* equip)
2389 {
2390  for (xmlNode_t* node = cgi->XML_GetNode(parent, SAVE_BASES_ITEM); node; node = cgi->XML_GetNextNode(node, parent, SAVE_BASES_ITEM)) {
2391  const char* s = cgi->XML_GetString(node, SAVE_BASES_ODS_ID);
2392  const objDef_t* od = INVSH_GetItemByID(s);
2393  if (!od) {
2394  cgi->Com_Printf("B_Load: Could not find item '%s'\n", s);
2395  continue;
2396  }
2397  equip->numItems[od->idx] = cgi->XML_GetInt(node, SAVE_BASES_NUM, 0);
2398  equip->numItemsLoose[od->idx] = cgi->XML_GetInt(node, SAVE_BASES_NUMLOOSE, 0);
2399  }
2400  return true;
2401 }
2402 
2407 bool B_LoadXML (xmlNode_t* parent)
2408 {
2409  int buildingIdx;
2410  xmlNode_t* bases;
2411 
2412  bases = cgi->XML_GetNode(parent, "bases");
2413  if (!bases) {
2414  cgi->Com_Printf("Error: Node 'bases' wasn't found in savegame\n");
2415  return false;
2416  }
2417 
2418  ccs.numBases = 0;
2419 
2420  cgi->Com_RegisterConstList(saveBaseConstants);
2421  FOREACH_XMLNODE(base, bases, SAVE_BASES_BASE) {
2422  const int i = ccs.numBases;
2423  base_t* const b = B_GetBaseByIDX(i);
2424  if (b == nullptr)
2425  break;
2426 
2427  ccs.numBases++;
2428 
2429  b->idx = cgi->XML_GetInt(base, SAVE_BASES_IDX, -1);
2430  if (b->idx < 0) {
2431  cgi->Com_Printf("Invalid base index %i\n", b->idx);
2432  cgi->Com_UnregisterConstList(saveBaseConstants);
2433  return false;
2434  }
2435  b->founded = true;
2436  const char* str = cgi->XML_GetString(base, SAVE_BASES_BASESTATUS);
2437  if (!cgi->Com_GetConstIntFromNamespace(SAVE_BASESTATUS_NAMESPACE, str, (int*) &b->baseStatus)) {
2438  cgi->Com_Printf("Invalid base status '%s'\n", str);
2439  cgi->Com_UnregisterConstList(saveBaseConstants);
2440  return false;
2441  }
2442 
2443  Q_strncpyz(b->name, cgi->XML_GetString(base, SAVE_BASES_NAME), sizeof(b->name));
2444  cgi->XML_GetPos3(base, SAVE_BASES_POS, b->pos);
2445  b->alienInterest = cgi->XML_GetFloat(base, SAVE_BASES_ALIENINTEREST, 0.0);
2446  b->aircraftCurrent = nullptr;
2447 
2448  /* building space */
2450  FOREACH_XMLNODE(snode, node, SAVE_BASES_BUILDING) {
2452  const int x = cgi->XML_GetInt(snode, SAVE_BASES_X, 0);
2453  const int y = cgi->XML_GetInt(snode, SAVE_BASES_Y, 0);
2454  baseBuildingTile_t* tile = &b->map[x][y];
2455  buildingIdx = cgi->XML_GetInt(snode, SAVE_BASES_BUILDINGINDEX, -1);
2456 
2457  tile->posX = y; /* NOT a typo ! */
2458  tile->posY = x;
2459  if (buildingIdx != -1)
2460  /* The buildings are actually parsed _below_. (See PRE_MAXBUI loop) */
2461  tile->building = B_GetBuildingByIDX(i, buildingIdx);
2462  else
2463  tile->building = nullptr;
2464  tile->blocked = cgi->XML_GetBool(snode, SAVE_BASES_BLOCKED, false);
2465  if (tile->blocked && tile->building != nullptr) {
2466  cgi->Com_Printf("inconstent base layout found\n");
2467  cgi->Com_UnregisterConstList(saveBaseConstants);
2468  return false;
2469  }
2470  }
2471  /* buildings */
2472  node = cgi->XML_GetNode(base, SAVE_BASES_BUILDINGS);
2473 
2474  ccs.numBuildings[i] = 0;
2475  FOREACH_XMLNODE(snode, node, SAVE_BASES_BUILDING) {
2476  const int buildId = cgi->XML_GetInt(snode, SAVE_BASES_BUILDING_PLACE, MAX_BUILDINGS);
2477  building_t* building;
2478  const building_t* buildingTemplate;
2479  char buildingType[MAX_VAR];
2480 
2481  if (buildId >= MAX_BUILDINGS) {
2482  cgi->Com_Printf("building ID is greater than MAX buildings\n");
2483  cgi->Com_UnregisterConstList(saveBaseConstants);
2484  return false;
2485  }
2486 
2487  Q_strncpyz(buildingType, cgi->XML_GetString(snode, SAVE_BASES_BUILDINGTYPE), sizeof(buildingType));
2488  if (buildingType[0] == '\0') {
2489  cgi->Com_Printf("No buildingtype set\n");
2490  cgi->Com_UnregisterConstList(saveBaseConstants);
2491  return false;
2492  }
2493 
2494  buildingTemplate = B_GetBuildingTemplate(buildingType);
2495  if (!buildingTemplate)
2496  continue;
2497 
2498  ccs.buildings[i][buildId] = *buildingTemplate;
2499  building = B_GetBuildingByIDX(i, buildId);
2500  building->idx = B_GetBuildingIDX(b, building);
2501  if (building->idx != buildId) {
2502  cgi->Com_Printf("building ID doesn't match\n");
2503  cgi->Com_UnregisterConstList(saveBaseConstants);
2504  return false;
2505  }
2506  building->base = b;
2507 
2509  if (!cgi->Com_GetConstIntFromNamespace(SAVE_BUILDINGSTATUS_NAMESPACE, str, (int*) &building->buildingStatus)) {
2510  cgi->Com_Printf("Invalid building status '%s'\n", str);
2511  cgi->Com_UnregisterConstList(saveBaseConstants);
2512  return false;
2513  }
2514 
2515  cgi->XML_GetDate(snode, SAVE_BASES_BUILDINGTIMESTART, &building->timeStart.day, &building->timeStart.sec);
2516 
2517  building->buildTime = cgi->XML_GetInt(snode, SAVE_BASES_BUILDINGBUILDTIME, 0);
2518  building->level = cgi->XML_GetFloat(snode, SAVE_BASES_BUILDINGLEVEL, 0);
2519  cgi->XML_GetPos2(snode, SAVE_BASES_POS, building->pos);
2520  ccs.numBuildings[i]++;
2521  }
2522 
2524  /* read missile battery slots */
2525  node = cgi->XML_GetNode(base, SAVE_BASES_BATTERIES);
2526  if (node)
2528  /* read laser battery slots */
2529  node = cgi->XML_GetNode(base, SAVE_BASES_LASERS);
2530  if (node)
2532  /* read equipment */
2533  node = cgi->XML_GetNode(base, SAVE_BASES_STORAGE);
2534  B_LoadStorageXML(node, &(b->storage));
2535  /* read radar info */
2537  RADAR_Initialise(&b->radar, cgi->XML_GetInt(base, SAVE_BASES_RADARRANGE, 0), cgi->XML_GetInt(base, SAVE_BASES_TRACKINGRANGE, 0), B_GetMaxBuildingLevel(b, B_RADAR), true);
2538 
2540  if (node) {
2541  b->alienContainment = new AlienContainment(CAP_Get(b, CAP_ALIENS), nullptr);
2542  b->alienContainment->load(node);
2543  }
2544 
2545  /* clear the mess of stray loaded pointers */
2546  b->bEquipment.init();
2547  }
2548  cgi->Com_UnregisterConstList(saveBaseConstants);
2549  cgi->Cvar_SetValue("mn_base_count", B_GetCount());
2550  return true;
2551 }
2552 
2559 {
2560  /* antimatter is stored in antimatter storage */
2561  if (obj->isVirtual || Q_streq(obj->id, ANTIMATTER_ITEM_ID))
2562  return false;
2563 
2564  return true;
2565 }
2566 
2574 int B_AddToStorage (base_t* base, const objDef_t* obj, int amount)
2575 {
2576  capacities_t* cap;
2577 
2578  assert(base);
2579  assert(obj);
2580 
2581  if (!B_ItemIsStoredInBaseStorage(obj))
2582  return 0;
2583 
2584  cap = CAP_Get(base, CAP_ITEMS);
2585  if (amount > 0) {
2586  if (obj->size > 0)
2587  cap->cur += (amount * obj->size);
2588  base->storage.numItems[obj->idx] += amount;
2589  } else if (amount < 0) {
2590  /* correct amount */
2591  const int itemInBase = B_ItemInBase(obj, base);
2592  amount = std::max(amount, -itemInBase);
2593  if (obj->size > 0)
2594  cap->cur += (amount * obj->size);
2595  base->storage.numItems[obj->idx] += amount;
2596 
2597  if (base->storage.numItems[obj->idx] == 0) {
2598  technology_t* tech = RS_GetTechForItem(obj);
2599  if (tech->statusResearch == RS_RUNNING && tech->base == base)
2600  RS_StopResearch(tech);
2601  }
2602  }
2603 
2604  return amount;
2605 }
2606 
2611 int B_AntimatterInBase (const base_t* base)
2612 {
2613 #ifdef DEBUG
2614  const objDef_t* od;
2615 
2617  if (od == nullptr)
2618  cgi->Com_Error(ERR_DROP, "Could not find " ANTIMATTER_ITEM_ID " object definition");
2619 
2620  assert(base);
2621  assert(B_ItemInBase(od, base) == CAP_GetCurrent(base, CAP_ANTIMATTER));
2622 #endif
2623 
2624  return CAP_GetCurrent(base, CAP_ANTIMATTER);
2625 }
2626 
2633 int B_AddAntimatter (base_t* base, int amount)
2634 {
2635  const objDef_t* od;
2636  capacities_t* cap;
2637 
2638  assert(base);
2639 
2641  if (od == nullptr)
2642  cgi->Com_Error(ERR_DROP, "Could not find " ANTIMATTER_ITEM_ID " object definition");
2643 
2644  cap = CAP_Get(base, CAP_ANTIMATTER);
2645  if (amount > 0) {
2646  cap->cur += amount;
2647  base->storage.numItems[od->idx] += amount;
2648  } else if (amount < 0) {
2649  /* correct amount */
2650  const int inBase = B_AntimatterInBase(base);
2651  amount = std::max(amount, -inBase);
2652  cap->cur += (amount);
2653  base->storage.numItems[od->idx] += amount;
2654  }
2655 
2656  return B_AntimatterInBase(base);
2657 }
#define MAX_BASEBUILDINGS
Definition: cp_base.h:39
static void B_UpdateAllBaseBuildingStatus(building_t *building, buildingStatus_t status)
Updates base status for particular buildings as well as capacities.
Definition: cp_base.cpp:978
#define SAVE_BASES_BUILDING_PLACE
Definition: save_base.h:45
bool B_SaveXML(xmlNode_t *parent)
Save callback for saving in xml format.
Definition: cp_base.cpp:2264
const objDef_t * INVSH_GetItemByIDSilent(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn't found.
Definition: inv_shared.cpp:249
#define FOREACH_XMLNODE(var, node, name)
Definition: cp_save.h:54
static const constListEntry_t saveBaseConstants[]
Definition: save_base.h:70
static void B_AddMap(char *maps, size_t mapsLength, char *coords, size_t coordsLength, const char *map, int col, int row)
Adds a map to the given position to the map string.
Definition: cp_base.cpp:544
#define SAVE_BASES_BASES
Definition: save_base.h:27
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:2134
const float RADAR_BASERANGE
Definition: cp_radar.cpp:39
base_t bases[MAX_BASES]
Definition: cp_campaign.h:281
abilityskills_t
Definition: chr_shared.h:36
const char * id
Definition: cp_building.h:78
void RADAR_Initialise(radar_t *radar, float range, float trackingRange, float level, bool updateSourceRadarMap)
Set radar range to new value.
Definition: cp_radar.cpp:239
A building with all it's data.
Definition: cp_building.h:73
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...
int numUFOs
Definition: cp_radar.h:38
chrScoreGlobal_t score
Definition: chr_shared.h:387
void B_UpdateBuildingConstructions(void)
Updates base data.
Definition: cp_base.cpp:2010
char * onDisable
Definition: cp_building.h:100
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't found.
Definition: inv_shared.cpp:282
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
bool B_GetBuildingStatus(const base_t *const base, const buildingType_t buildingType)
Get the status associated to a building.
Definition: cp_base.cpp:477
static void B_AddBlockedTiles(base_t *base, int count)
Fuction to put blocked tiles on basemap.
Definition: cp_base.cpp:201
char * id
Definition: cp_aircraft.h:119
aircraft_t * AIR_NewAircraft(base_t *base, const aircraft_t *aircraftTemplate)
Places a new aircraft in the given base.
float liquidationRate
Definition: cp_campaign.h:199
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
bool E_HireEmployeeByType(base_t *base, employeeType_t type)
Hires the first free employee of that type.
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
float B_GetMaxBuildingLevel(const base_t *base, const buildingType_t type)
Get the maximum level of a building type in a base.
Definition: cp_base.cpp:518
int B_GetNumberOfBuildingsInBaseByTemplate(const base_t *base, const building_t *tpl)
Counts the number of buildings of a particular type in a base.
Definition: cp_base.cpp:1353
int experience[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:120
#define SAVE_BASES_BUILDINGS
Definition: save_base.h:43
#define _(String)
Definition: cl_shared.h:43
int E_CountAllHired(const base_t *const base, const bool peopleOnly)
Counts all hired employees of a given base.
researchStatus_t statusResearch
Definition: cp_research.h:163
void * data
Definition: list.h:31
weaponFireDefIndex_t weapFdsIdx
Definition: inv_shared.h:126
int numBuildingTemplates
Definition: cp_campaign.h:339
void AIM_AutoEquipAircraft(aircraft_t *aircraft)
Auto Add weapon and ammo to an aircraft.
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
int B_AddToStorage(base_t *base, const objDef_t *obj, int amount)
Add/remove items to/from the storage.
Definition: cp_base.cpp:2574
char name[MAX_VAR]
Definition: cp_base.h:86
bool B_IsBuildingDestroyable(const building_t *building)
Returns if a base building is destroyable.
Definition: cp_base.cpp:223
bool founded
Definition: cp_base.h:90
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
Definition: inv_shared.cpp:576
int credits
Definition: cp_campaign.h:242
void empty(void)
Empties the cargo.
Definition: itemcargo.cpp:99
char firstBaseTemplate[MAX_VAR]
Definition: cp_campaign.h:192
#define CAP_Get(base, capacity)
Capacity macros.
Definition: cp_capacity.h:50
int maxTeamSize
Definition: cp_aircraft.h:138
void B_ResetAllStatusAndCapacities(base_t *base, bool firstEnable)
Recalculate status and capacities of one base.
Definition: cp_base.cpp:705
void RS_MarkResearchable(const base_t *base, bool init)
Marks all the techs that can be researched. Automatically researches 'free' techs such as ammo for a ...
void B_UpdateBaseCapacities(baseCapacities_t cap, base_t *base)
Updates base capacities.
Definition: cp_base.cpp:2161
csi_t * csi
Definition: cgame.h:100
const building_t * B_GetBuildingInBaseByType(const base_t *base, buildingType_t buildingType, bool onlyWorking)
Gets a building of a given type in the given base.
Definition: cp_base.cpp:1414
const float RADAR_BASETRACKINGRANGE
Definition: cp_radar.cpp:40
baseStatus_t baseStatus
Definition: cp_base.h:102
#define SAVE_BASES_X
Definition: save_base.h:38
base_t * B_GetCurrentSelectedBase(void)
returns the currently selected base
Definition: cp_base.cpp:1578
int day
Definition: common.h:291
#define BASE_SIZE
Definition: cp_base.h:38
static bool B_CheckBuildingConstruction(building_t *building)
Checks whether the construction of a building is finished. Calls the onEnable functions and assign wo...
Definition: cp_base.cpp:1988
date_t date
Definition: cp_campaign.h:245
xmlNode_t *IMPORT * XML_GetPos3(xmlNode_t *parent, const char *name, vec3_t pos)
bool B_LoadXML(xmlNode_t *parent)
Loads base data.
Definition: cp_base.cpp:2407
const objDef_t * def(void) const
Definition: inv_shared.h:469
struct base_s * base
Definition: cp_research.h:167
void INS_SetCurrentSelectedInstallation(const installation_t *installation)
Sets the currently selected installation.
class AlienContainment * alienContainment
Definition: cp_base.h:108
#define MAX_BASE_SLOT
Definition: cp_base.h:35
void AIR_MoveAircraftIntoNewHomebase(aircraft_t *aircraft, base_t *base)
Moves a given aircraft to a new base (also the employees and inventory)
const aircraft_t * AIR_GetAircraft(const char *name)
Searches the global array of aircraft types for a given aircraft.
fireDef_t fd[MAX_WEAPONS_PER_OBJDEF][MAX_FIREDEFS_PER_WEAPON]
Definition: inv_shared.h:314
const byte * GEO_GetColor(const vec2_t pos, mapType_t type, bool *coast)
Returns the color value from geoscape of a certain mask (terrain, culture or population) at a given p...
char equipment[MAX_VAR]
Definition: cp_campaign.h:171
void AL_AddAliens(aircraft_t *aircraft)
Puts alien cargo into Alien Containment.
#define ABILITY_NUM_TYPES
Definition: chr_shared.h:54
bool GEO_IsRadarOverlayActivated(void)
Definition: cp_geoscape.cpp:84
void E_HireForBuilding(base_t *base, building_t *building, int num)
Hires some employees of appropriate type for a building.
int numODs
Definition: q_shared.h:518
#define SAVE_BASES_ITEM
Definition: save_base.h:58
void CP_MissionNotifyBaseDestroyed(const base_t *base)
Notify that a base has been removed.
#define SAVE_BASES_BASESTATUS
Definition: save_base.h:33
buildingType_t
All different building types.
Definition: cp_building.h:51
template for creating a base
Definition: cp_base.h:130
#define SAVE_BASES_BUILDINGSPACE
Definition: save_base.h:36
static void B_MoveAircraftOnGeoscapeToOtherBases(const base_t *base)
Will ensure that aircraft on geoscape are not stored in a base that no longer has any hangar left...
Definition: cp_base.cpp:860
Item cargo class header.
linkedList_t * acTeam
Definition: cp_aircraft.h:139
int basesBuilt
Definition: cp_statistics.h:32
static void B_AddBuildingToBasePos(base_t *base, const building_t *buildingTemplate, bool hire, const vec2_t pos)
Build starting building in the first base, and hire employees.
Definition: cp_base.cpp:1010
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
Alien containment class.
baseCapacities_t AIR_GetHangarCapacityType(const aircraft_t *aircraft)
Returns capacity type needed for an aircraft.
#define SAVE_BASES_LASERS
Definition: save_base.h:52
building_t * B_GetBuildingTemplate(const char *buildingName)
Returns the building in the global building-types list that has the unique name buildingID.
linkedList_t * initialCraft
Definition: cp_campaign.h:209
void B_BaseResetStatus(base_t *const base)
Sets the baseStatus to BASE_NOT_USED.
Definition: cp_base.cpp:1789
#define SAVE_BASES_BATTERIES
Definition: save_base.h:51
equipDef_t storage
Definition: cp_base.h:112
uiMessageListNodeMessage_t * MS_AddNewMessage(const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup, bool playSound)
Adds a new message to message stack.
Definition: cp_messages.cpp:61
static void B_InitialEquipment(aircraft_t *aircraft, const equipDef_t *ed)
Prepares initial equipment for initial team the beginning of the campaign.
Definition: cp_base.cpp:1759
memPool_t * cp_campaignPool
Definition: cp_campaign.cpp:61
int buildTime
Definition: cp_building.h:92
#define CAP_GetCurrent(base, capacity)
Definition: cp_capacity.h:52
building_t * B_BuildBuilding(base_t *base, const building_t *buildingTemplate, int col, int row)
Build a new building to the base.
Definition: cp_base.cpp:1279
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
class ItemCargo * itemCargo
Definition: cp_aircraft.h:177
bool autofire
Definition: cp_base.h:80
void GEO_ResetAction(void)
No more special action on the geoscape.
Header file for menu related console command callbacks.
static void CL_SwapSkill(character_t *cp1, character_t *cp2, abilityskills_t skill)
Swaps one skill from character1 to character 2 and vice versa.
Definition: cp_base.cpp:1622
int CAP_GetFreeCapacity(const base_t *base, baseCapacities_t capacityType)
Returns the free capacity of a type.
struct radar_s radar
Definition: cp_aircraft.h:158
#define xmlNode_t
Definition: xml.h:24
int B_GetCount(void)
Returns the count of founded bases.
Definition: cp_base.cpp:276
void RADAR_InitialiseUFOs(radar_t *radar)
Reset UFO sensored on radar.
Definition: cp_radar.cpp:265
#define SAVE_BASES_TARGET
Definition: save_base.h:55
const struct building_s * dependsBuilding
Definition: cp_building.h:112
void CAP_AddMax(base_t *base, baseCapacities_t capacity, int value)
Changes the maximal capacity on a base.
Definition: cp_capacity.cpp:86
#define SAVE_BASES_BUILDINGSTATUS
Definition: save_base.h:46
item instance data, with linked list capability
Definition: inv_shared.h:402
#define REMOVE_ELEM(array, index, n)
Definition: common.h:408
#define SAVE_BASES_BASE
Definition: save_base.h:29
void B_ParseBaseTemplate(const char *name, const char **text)
Reads a base layout template.
Definition: cp_base.cpp:1435
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
int RS_CountScientistsInBase(const base_t *base)
Returns the number of employees searching in labs in given base.
vec2_t pos
Definition: cp_building.h:104
xmlNode_t *IMPORT * XML_GetDate(xmlNode_t *parent, const char *name, int *day, int *sec)
#define SAVE_BASES_TRACKINGRANGE
Definition: save_base.h:64
void AII_ReloadAircraftWeapons(aircraft_t *aircraft)
Reload the weapons of an aircraft.
cvar_t *IMPORT * Cvar_Set(const char *varName, const char *value,...) __attribute__((format(__printf__
Item * getHolsterContainer() const
Definition: inv_shared.cpp:970
#define MAX_BUILDINGS
Definition: cp_base.h:34
bool B_MapIsCellFree(const base_t *base, int col, int row)
Check a base cell.
Definition: cp_base.cpp:1263
aircraft_t * aircraftCurrent
Definition: cp_base.h:100
#define ngettext(x, y, cnt)
Definition: g_local.h:40
#define ERR_DROP
Definition: common.h:211
void B_DumpAircraftToHomeBase(aircraft_t *aircraft)
Will unload all cargo to the homebase.
Definition: cp_base.cpp:2085
static void B_UpdateAntimatterCap(base_t *base)
Update Antimatter Capacity.
Definition: cp_base.cpp:693
bool mandatory
Definition: cp_building.h:105
#define DEBUG_CLIENT
Definition: defines.h:59
void B_Delete(base_t *base)
Resets a base structure.
Definition: cp_base.cpp:897
building_t * building
Definition: cp_base.h:69
GLsizei size
Definition: r_gl.h:152
char * id
Definition: cp_base.h:131
struct base_s * base
Definition: cp_building.h:76
#define OBJZERO(obj)
Definition: shared.h:178
#define SAVE_BASES_NUM
Definition: save_base.h:60
const equipDef_t *IMPORT * INV_GetEquipmentDefinitionByID(const char *name)
#define MAX_VAR
Definition: shared.h:36
Item * getRightHandContainer() const
Definition: inv_shared.cpp:955
aircraft_t * AIR_GetFirstFromBase(const base_t *b)
Iterates through the aircraft of a base.
Definition: cp_aircraft.cpp:50
bool B_SaveStorageXML(xmlNode_t *parent, const equipDef_t &equip)
Saves base storage.
Definition: cp_base.cpp:2245
#define Vector2Set(v, x, y)
Definition: vector.h:61
bool B_CheckBuildingDependencesStatus(const building_t *building)
Check that the dependences of a building is operationnal.
#define SAVE_BASES_NAME
Definition: save_base.h:31
int numLasers
Definition: cp_base.h:120
Campaign missions headers.
static linkedList_t * B_GetNeighbours(const building_t *building)
Returns the neighbourhood of a building.
Definition: cp_base.cpp:57
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition: cp_base.cpp:285
#define MAX_BLOCKEDFIELDS
Definition: cp_base.h:41
void RS_MarkCollected(technology_t *tech)
Marks a give technology as collected.
#define SAVE_BASES_AUTOFIRE
Definition: save_base.h:54
#define SAVE_BASES_ALIENCONTAINMENT
Definition: save_base.h:66
const cgame_import_t * cgi
void UFO_NotifyPhalanxAircraftRemoved(const aircraft_t *const aircraft)
Notify to UFOs that a Phalanx aircraft has been destroyed.
Definition: cp_ufo.cpp:972
struct radar_s radar
Definition: cp_base.h:106
bool B_LoadStorageXML(xmlNode_t *parent, equipDef_t *equip)
Loads base storage.
Definition: cp_base.cpp:2388
void B_AircraftReturnedToHomeBase(aircraft_t *aircraft)
Do anything when dropship returns to base.
Definition: cp_base.cpp:2103
int idx
Definition: cp_base.h:85
bool selected
Definition: cp_base.h:126
#define SAVE_BASES_BUILDINGLEVEL
Definition: save_base.h:49
void B_SetUpFirstBase(const campaign_t *campaign, base_t *base)
Setup aircraft and equipment for first base. Uses the campaign scriptable equipmentlist.
Definition: cp_base.cpp:1107
int initialSkills[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:123
void CP_UpdateCredits(int credits)
Sets credits and update mn_credits cvar.
const char *IMPORT * Com_GetConstVariable(const char *space, int value)
This is the technology parsed from research.ufo.
Definition: cp_research.h:137
mapAction_t mapAction
Definition: cp_campaign.h:260
ccs_t ccs
Definition: cp_campaign.cpp:62
stats_t campaignStats
Definition: cp_campaign.h:378
#define SAVE_BASES_ALIENINTEREST
Definition: save_base.h:34
int fixCosts
Definition: cp_building.h:83
void B_SaveBaseSlotsXML(const baseWeapon_t *weapons, const int numWeapons, xmlNode_t *node)
Saves the missile and laser slots of a base or sam site.
Definition: cp_base.cpp:2229
#define SAVE_BASES_Y
Definition: save_base.h:39
static void CL_SwapSkills(linkedList_t *team)
Swaps skills of the initial team of soldiers so that they match inventories.
Definition: cp_base.cpp:1719
base_t * B_GetBaseByIDX(int baseIdx)
Array bound check for the base index. Will also return unfounded bases as long as the index is in the...
Definition: cp_base.cpp:312
Campaign geoscape time header.
baseBuildingTile_t map[BASE_SIZE][BASE_SIZE]
Definition: cp_base.h:87
float level
Definition: cp_building.h:89
#define SAVE_BASES_IDX
Definition: save_base.h:30
char * onDestroy
Definition: cp_building.h:98
void CAP_CheckOverflow(void)
Checks capacity overflows on bases.
Header for Geoscape management.
#define MAX_INSTALLATIONS_PER_BASE
#define BASE_TILE_UNITS
Definition: cp_base.h:49
bool BS_SellItem(const objDef_t *od, base_t *base, int count)
Sells items from the market.
Definition: cp_market.cpp:413
void B_Destroy(base_t *base)
Destroy a base.
Definition: cp_base.cpp:913
void B_SetCurrentSelectedBase(const base_t *base)
Sets the selected base.
Definition: cp_base.cpp:1553
char * name
Definition: cp_building.h:79
void init()
Definition: inv_shared.cpp:703
static void B_SellOrAddItems(aircraft_t *aircraft)
Sell items to the market or add them to base storage.
Definition: cp_base.cpp:2031
int sec
Definition: common.h:292
#define SAVE_BUILDINGSTATUS_NAMESPACE
Definition: save_base.h:69
int numBases
Definition: cp_campaign.h:282
char cp_messageBuffer[MAX_MESSAGE_TEXT]
Definition: cp_messages.cpp:31
bool AIR_IsAircraftOnGeoscape(const aircraft_t *aircraft)
Checks whether given aircraft is on geoscape.
Alien containment class header.
int B_GetNumberOfBuildingsInBaseByBuildingType(const base_t *base, const buildingType_t buildingType)
Counts the number of buildings of a particular building type in a base.
Definition: cp_base.cpp:1386
bool B_CheckBuildingTypeStatus(const base_t *const base, buildingType_t type, buildingStatus_t status, int *cnt)
Searches the base for a given building type with the given status.
Definition: cp_base.cpp:389
int numBuildings[MAX_BASES]
Definition: cp_campaign.h:343
QGL_EXTERN GLuint count
Definition: r_gl.h:99
#define AIR_ForeachFromBase(var, base)
iterates trough all aircraft from a specific homebase
Definition: cp_aircraft.h:201
#define CAP_GetMax(base, capacity)
Definition: cp_capacity.h:51
struct base_s * homebase
Definition: cp_aircraft.h:149
baseCapacities_t
All possible capacities in base.
Definition: cp_capacity.h:27
bool B_IsBuildingBuiltUp(const building_t *building)
Returns if a building is fully buildt up.
Definition: cp_building.cpp:36
baseWeapon_t batteries[MAX_BASE_SLOT]
Definition: cp_base.h:116
int numBuildings
Definition: cp_base.h:133
void AIR_DestroyAircraft(aircraft_t *aircraft, bool killPilot)
Removes an aircraft from its base and the game.
void CAP_UpdateStorageCap(base_t *base)
Update Storage Capacity.
Definition: cp_capacity.cpp:52
void CP_SpawnRescueMission(aircraft_t *aircraft, aircraft_t *ufo)
Spawn a new rescue mission for a crashed (phalanx) aircraft.
#define MIN_BLOCKEDFIELDS
Definition: cp_base.h:42
vec2_t size
Definition: cp_building.h:82
#define B_GetBuildingByIDX(baseIdx, buildingIdx)
Definition: cp_base.cpp:45
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition: parse.cpp:107
aircraft_t * UFO_GetByIDX(const int idx)
returns the UFO on the geoscape with a certain index
Definition: cp_ufo.cpp:85
xmlNode_t *IMPORT * XML_GetPos2(xmlNode_t *parent, const char *name, vec2_t pos)
static bool B_UpdateStatusBuilding(base_t *base, buildingType_t buildingType, bool onBuilt)
Update status of every building when a building has been built/destroyed.
Definition: cp_base.cpp:634
#define SAVE_BASES_RADARRANGE
Definition: save_base.h:63
#define SAVE_BASES_POS
Definition: save_base.h:32
bool isReloadable() const
Definition: inv_shared.h:479
static bool B_CheckUpdateBuilding(building_t *building)
Check base status for particular buildings as well as capacities.
Definition: cp_base.cpp:599
struct building_s * tpl
Definition: cp_building.h:75
Header file for aircraft stuff.
int B_AntimatterInBase(const base_t *base)
returns the amount of antimatter stored in a base
Definition: cp_base.cpp:2611
int B_LoadBaseSlotsXML(baseWeapon_t *weapons, int max, xmlNode_t *p)
Loads the missile and laser slots of a base or sam site.
Definition: cp_base.cpp:2348
int B_GetInstallationLimit(void)
Counts the actual installation count limit.
Definition: cp_base.cpp:1153
#define B_GetBuildingAt(base, x, y)
Definition: cp_base.h:165
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
QGL_EXTERN GLint i
Definition: r_gl.h:113
base_t * B_GetFirstUnfoundedBase(void)
Get the first unfounded base.
Definition: cp_base.cpp:1537
void AII_LoadOneSlotXML(xmlNode_t *node, aircraftSlot_t *slot, bool weapon)
Loads one slot (base, installation or aircraft)
void CAP_SetCurrent(base_t *base, baseCapacities_t capacity, int value)
Sets the current (used) capacity on a base.
Definition: cp_capacity.cpp:97
date_t timeStart
Definition: cp_building.h:91
xmlNode_t *IMPORT * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
static void B_BuildFromTemplate(base_t *base, const char *templateName, bool hire)
builds a base from template
Definition: cp_base.cpp:1040
int numItems[MAX_OBJDEFS]
Definition: inv_shared.h:608
Header for slot management related stuff.
void AIR_AssignInitial(aircraft_t *aircraft)
Assigns initial team of soldiers to aircraft.
int BS_GetItemSellingPrice(const objDef_t *od)
Get the price for an item that you want to sell on the market.
Definition: cp_market.cpp:89
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
bool B_BaseHasItem(const base_t *base, const objDef_t *item)
Check if an item is available on a base.
Definition: cp_base.cpp:2120
void RS_StopResearch(technology_t *tech)
Stops a research (Removes scientists from it)
Definition: cp_research.cpp:93
#define BASE_INITIALINTEREST
Definition: cp_base.h:51
bool B_PostLoadInit(void)
Set the capacity stuff for all the bases after loading a savegame.
Definition: cp_base.cpp:2378
int size
Definition: inv_shared.h:334
int range
Definition: cp_radar.h:35
float alienInterest
Definition: cp_base.h:104
#define SAVE_BASES_WEAPON
Definition: save_base.h:53
int B_AddAntimatter(base_t *base, int amount)
Manages antimatter (adding, removing) through Antimatter Storage Facility.
Definition: cp_base.cpp:2633
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition: shared.cpp:475
static bool B_PostLoadInitCapacity(void)
Set the capacity stuff for all the bases after loading a savegame.
Definition: cp_base.cpp:2365
#define SAVE_BASES_BUILDING
Definition: save_base.h:37
building_t * B_GetNextBuildingByType(const base_t *base, building_t *lastBuilding, buildingType_t buildingType)
Iterates throught buildings of a type in a base.
Definition: cp_base.cpp:366
#define MapIsWater(color)
Definition: cp_geoscape.h:32
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
int weaponSkill
Definition: inv_shared.h:162
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
building_t buildingTemplates[MAX_BUILDINGS]
Definition: cp_campaign.h:338
#define Vector2Copy(src, dest)
Definition: vector.h:52
vec_t vec2_t[2]
Definition: ufotypes.h:38
linkedList_t * next
Definition: list.h:32
Header file for single player campaign control.
byte numItemsLoose[MAX_OBJDEFS]
Definition: inv_shared.h:609
void B_InitStartup(void)
Resets console commands.
Definition: cp_base.cpp:1971
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
#define SAVE_BASES_BUILDINGTYPE
Definition: save_base.h:44
Inventory inv
Definition: chr_shared.h:392
char soldierEquipment[MAX_VAR]
Definition: cp_campaign.h:170
void E_DeleteAllEmployees(base_t *base)
Removes all employees completely from the game (buildings + global list) from a given base...
bool load(xmlNode_t *root)
Load alien cargo from xml savegame.
Definition: aliencargo.cpp:167
baseWeapon_t lasers[MAX_BASE_SLOT]
Definition: cp_base.h:119
xmlNode_t *IMPORT * XML_AddNode(xmlNode_t *parent, const char *name)
const char *IMPORT * Com_EParse(const char **text, const char *errhead, const char *errinfo)
bool B_AssembleMap(char *maps, size_t mapsLength, char *coords, size_t coordsLength, const base_t *base)
Perform the base assembling in case of an alien attack.
Definition: cp_base.cpp:562
static bool B_AddBlockedTile(base_t *base, int row, int column)
Check and add blocked tile to the base.
Definition: cp_base.cpp:137
char name[MAX_VAR]
Definition: chr_shared.h:371
#define B_GetBuildingIDX(base, building)
Definition: cp_base.cpp:46
bool autosell[MAX_OBJDEFS]
Definition: cp_market.h:33
void BDEF_InitialiseBaseSlots(base_t *base)
Initialise all values of base slot defence.
#define lengthof(x)
Definition: shared.h:105
market_t eMarket
Definition: cp_campaign.h:231
XML tag constants for savegame.
baseBuildingTile_t buildings[MAX_BASEBUILDINGS]
Definition: cp_base.h:132
building_t buildings[MAX_BASES][MAX_BUILDINGS]
Definition: cp_campaign.h:341
Inventory bEquipment
Definition: cp_base.h:114
#define Q_streq(a, b)
Definition: shared.h:136
vec3_t pos
Definition: cp_base.h:91
bool CP_CheckCredits(int costs)
Checks whether you have enough credits for something.
int stats[AIR_STATS_MAX]
Definition: cp_aircraft.h:159
Store capacities in base.
Definition: cp_capacity.h:41
buildingType_t buildingType
Definition: cp_building.h:110
const char * AIR_CheckMoveIntoNewHomebase(const aircraft_t *aircraft, const base_t *base)
Checks if destination base can store an aircraft and its team.
#define SAVE_BASES_BUILDINGINDEX
Definition: save_base.h:40
char *IMPORT * PoolStrDup(const char *in, memPool_t *pool, const int tagNum)
An aircraft with all it's data.
Definition: cp_aircraft.h:114
void CAP_SetMax(base_t *base, baseCapacities_t capacity, int value)
Sets the maximal capacity on a base.
Definition: cp_capacity.cpp:75
static void CL_DoSwapSkills(character_t *cp1, character_t *cp2, const abilityskills_t skill)
Definition: cp_base.cpp:1641
bool B_BuildingDestroy(building_t *building)
Removes a building from the given base.
Definition: cp_base.cpp:770
void AIR_MoveEmployeeInventoryIntoStorage(const aircraft_t &aircraft, equipDef_t &ed)
Move all the equipment carried by the team on the aircraft into the given equipment.
#define MAX_BASES
Definition: cp_base.h:32
#define SAVE_BASES_STORAGE
Definition: save_base.h:57
void PR_UpdateProductionCap(base_t *base, int workerChange)
Update the current capacity of Workshop.
Definition: cp_produce.cpp:606
technology_t * RS_GetTechForItem(const objDef_t *item)
Returns technology entry for an item.
campaign_t * curCampaign
Definition: cp_campaign.h:377
void GEO_SetOverlay(const char *overlayID, int status)
Turn overlay on/off.
buildingStatus_t buildingStatus
Definition: cp_building.h:94
linkedList_t * list(void) const
Returns a copy of the cargo list.
Definition: itemcargo.cpp:156
void B_SetBuildingStatus(base_t *const base, const buildingType_t buildingType, bool newStatus)
Set status associated to a building.
Definition: cp_base.cpp:498
#define SAVE_BASES_ODS_ID
Definition: save_base.h:59
bool save(xmlNode_t *root) const
Save alien cargo to xml savegame.
Definition: aliencargo.cpp:188
#define ANTIMATTER_ITEM_ID
Definition: cp_research.h:35
uint8_t byte
Definition: ufotypes.h:34
baseCapacities_t B_GetCapacityFromBuildingType(buildingType_t type)
Get the capacity associated to a building type.
Definition: cp_base.cpp:415
#define B_IsTileBlocked(base, x, y)
Definition: cp_base.h:164
bool hasBuilding[MAX_BUILDING_TYPE]
Definition: cp_base.h:98
#define MAX_BASETEMPLATES
Definition: cp_base.h:33
bool isVirtual
Definition: inv_shared.h:284
void B_SelectBase(const base_t *base)
Select and opens a base.
Definition: cp_base.cpp:1592
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
#define SAVE_BASES_BUILDINGTIMESTART
Definition: save_base.h:47
static int CL_GetSkillIndicator(const character_t *chr, abilityskills_t skill)
Assembles a skill indicator for the given character and its wore weapons in correlation to the given ...
Definition: cp_base.cpp:1682
void AII_SaveOneSlotXML(xmlNode_t *p, const aircraftSlot_t *slot, bool weapon)
Save callback for savegames in XML Format.
#define SAVE_BASES_BUILDINGBUILDTIME
Definition: save_base.h:48
base_t * B_Build(const campaign_t *campaign, const vec2_t pos, const char *name, bool fillBase)
Build new base, uses template for the first base.
Definition: cp_base.cpp:1185
aircraft_t * target
Definition: cp_base.h:79
baseTemplate_t baseTemplates[MAX_BASETEMPLATES]
Definition: cp_campaign.h:285
vec2_t pos
Definition: cp_missions.h:104
const char *IMPORT * Cmd_Argv(int n)
Definition: scripts.h:55
item cargo entry
Definition: itemcargo.h:32
const baseTemplate_t * B_GetBaseTemplate(const char *baseTemplateID)
Returns the baseTemplate in the global baseTemplate list that has the unique name baseTemplateID...
Definition: cp_base.cpp:1246
#define SAVE_BASESTATUS_NAMESPACE
Definition: save_base.h:68
char * onEnable
Definition: cp_building.h:99
int trackingRange
Definition: cp_radar.h:36
const char * mapPart
Definition: cp_building.h:80
const char * id
Definition: inv_shared.h:268
const char *IMPORT * XML_GetString(xmlNode_t *parent, const char *name)
xmlNode_t *IMPORT * XML_GetNode(xmlNode_t *parent, const char *name)
void AIRFIGHT_RemoveProjectileAimingAircraft(const aircraft_t *aircraft)
Set all projectile aiming a given aircraft to an idle destination.
const char * image
Definition: cp_building.h:80
bool B_FireEvent(const building_t *buildingTemplate, const base_t *base, buildingEvent_t eventType)
Run eventhandler script for a building.
#define SAVE_BASES_BLOCKED
Definition: save_base.h:41
int numBatteries
Definition: cp_base.h:117
void B_SetName(base_t *base, const char *name)
Set the base name.
Definition: cp_base.cpp:1173
level_locals_t level
Definition: g_main.cpp:38
buildingType_t B_GetBuildingTypeByCapacity(baseCapacities_t cap)
Get building type by base capacity.
Definition: cp_base.cpp:446
int numBaseTemplates
Definition: cp_campaign.h:286
#define SAVE_BASES_NUMLOOSE
Definition: save_base.h:61
building_t * B_GetNextBuilding(const base_t *base, building_t *lastBuilding)
Iterates through buildings in a base.
Definition: cp_base.cpp:338
bool B_ItemIsStoredInBaseStorage(const objDef_t *obj)
Check if an item is stored in storage.
Definition: cp_base.cpp:2558
void AIR_DeleteAircraft(aircraft_t *aircraft)
Removes an aircraft from its base and the game.
Describes a character with all its attributes.
Definition: chr_shared.h:369
buildingStatus_t
All possible building status.
Definition: cp_building.h:31