UFO: Alien Invasion
cp_base.cpp
Go to the documentation of this file.
1 
8 /*
9 Copyright (C) 2002-2022 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 "../../DateTime.h"
28 #include "../../cl_shared.h"
29 #include "../../cl_inventory.h" /* INV_GetEquipmentDefinitionByID */
30 #include "../../ui/ui_dataids.h"
31 #include "../../../shared/parse.h"
32 #include "cp_campaign.h"
33 #include "cp_mapfightequip.h"
34 #include "cp_aircraft.h"
35 #include "cp_missions.h"
36 #include "cp_geoscape.h"
37 #include "cp_popup.h"
38 #include "cp_radar.h"
39 #include "cp_time.h"
40 #include "cp_base_callbacks.h"
41 #include "cp_ufo.h"
42 #include "save/save_base.h"
43 #include "aliencontainment.h"
44 #include "itemcargo.h"
45 
46 #define B_GetBuildingByIDX(baseIdx, buildingIdx) (&ccs.buildings[(baseIdx)][(buildingIdx)])
47 #define B_GetBuildingIDX(base, building) ((ptrdiff_t)((building) - ccs.buildings[base->idx]))
48 #define B_GetBaseIDX(base) ((ptrdiff_t)((base) - ccs.bases))
49 
50 static void B_InitialEquipment(aircraft_t* aircraft, const equipDef_t* ed);
51 
58 static linkedList_t* B_GetNeighbours (const building_t* building)
59 {
60  if (!building || !B_IsBuildingBuiltUp(building))
61  return nullptr;
62 
63  const int x = building->pos[0];
64  const int y = building->pos[1];
65  base_t* base = building->base;
66  linkedList_t* neighbours = nullptr;
67 
68  assert(base);
69  for (int i = x; i < x + building->size[0]; i++) {
70  /* North */
71  if (y > 0 && B_GetBuildingAt(base, i, y - 1) != nullptr)
72  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, i, y - 1));
73  /* South */
74  if (y < BASE_SIZE - building->size[1] && B_GetBuildingAt(base, i, y + building->size[1]) != nullptr)
75  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, i, y + building->size[1]));
76  }
77  for (int i = y; i < y + building->size[1]; i++) {
78  /* West */
79  if (x > 0 && B_GetBuildingAt(base, x - 1, i) != nullptr)
80  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, x - 1, i));
81  /* East */
82  if (x < BASE_SIZE - building->size[0] && B_GetBuildingAt(base, x + building->size[0], i) != nullptr)
83  cgi->LIST_AddPointer(&neighbours, (void*)B_GetBuildingAt(base, x + building->size[0], i));
84  }
85  return neighbours;
86 }
87 
88 #ifdef DEBUG
89 
93 static bool B_IsCoherent (const base_t* base)
94 {
95  int found[MAX_BUILDINGS];
96  linkedList_t* queue = nullptr;
97  building_t* bldg = nullptr;
98 
99  OBJZERO(found);
100  while ((bldg = B_GetNextBuilding(base, bldg)) != nullptr) {
101  cgi->LIST_AddPointer(&queue, (void*)bldg);
102  break;
103  }
104  if (!bldg)
105  return true;
106 
107  while (!cgi->LIST_IsEmpty(queue)) {
108  bldg = (building_t*)queue->data;
109  found[bldg->idx] = 1;
110  cgi->LIST_RemoveEntry(&queue, queue);
111 
112  linkedList_t* neighbours = B_GetNeighbours(bldg);
113  LIST_Foreach(neighbours, building_t, bldg) {
114  if (found[bldg->idx] == 0) {
115  found[bldg->idx] = 1;
116  cgi->LIST_AddPointer(&queue, (void*)bldg);
117  }
118  }
119  cgi->LIST_Delete(&neighbours);
120  }
121  cgi->LIST_Delete(&queue);
122 
123  for (int i = 0; i < ccs.numBuildings[base->idx]; i++) {
124  if (found[i] != 1)
125  return false;
126  }
127  return true;
128 }
129 #endif
130 
138 static bool B_AddBlockedTile (base_t* base, int row, int column)
139 {
140  assert(base);
141 
142  if (B_GetBuildingAt(base, column, row) != nullptr)
143  return false;
144 
145  int found[BASE_SIZE][BASE_SIZE];
146  OBJZERO(found);
147  found[row][column] = -1;
148 
149  linkedList_t* queue = nullptr;
150  /* Get first non blocked tile */
151  for (int y = 0; y < BASE_SIZE && cgi->LIST_IsEmpty(queue); y++) {
152  for (int x = 0; x < BASE_SIZE && cgi->LIST_IsEmpty(queue); x++) {
153  if (x == column && y == row)
154  continue;
155  if (!B_IsTileBlocked(base, x, y))
156  cgi->LIST_AddPointer(&queue, &base->map[y][x]);
157  }
158  }
159 
160  if (cgi->LIST_IsEmpty(queue))
161  return false;
162 
163  /* BS Traversal */
164  while (!cgi->LIST_IsEmpty(queue)) {
165  baseBuildingTile_t* tile = (baseBuildingTile_t*)queue->data;
166  cgi->LIST_RemoveEntry(&queue, queue);
167  const int x = tile->posX;
168  const int y = tile->posY;
169 
170  found[y][x] = 1;
171  /* West */
172  if (x > 0 && !B_IsTileBlocked(base, x - 1, y) && found[y][x - 1] == 0)
173  cgi->LIST_AddPointer(&queue, (void*)&base->map[y][x - 1]);
174  /* East */
175  if (x < BASE_SIZE - 1 && !B_IsTileBlocked(base, x + 1, y) && found[y][x + 1] == 0)
176  cgi->LIST_AddPointer(&queue, (void*)&base->map[y][x + 1]);
177  /* North */
178  if (y > 0 && !B_IsTileBlocked(base, x, y - 1) && found[y - 1][x] == 0)
179  cgi->LIST_AddPointer(&queue, (void*)&base->map[y - 1][x]);
180  /* South */
181  if (y < BASE_SIZE - 1 && !B_IsTileBlocked(base, x, y + 1) && found[y + 1][x] == 0)
182  cgi->LIST_AddPointer(&queue, (void*)&base->map[y + 1][x]);
183  }
184  cgi->LIST_Delete(&queue);
185 
186  /* Check for unreached areas */
187  for (int y = 0; y < BASE_SIZE; y++) {
188  for (int x = 0; x < BASE_SIZE; x++) {
189  if (!B_IsTileBlocked(base, x, y) && found[y][x] == 0)
190  return false;
191  }
192  }
193  base->map[row][column].blocked = true;
194  return true;
195 }
196 
202 static void B_AddBlockedTiles (base_t* base, int count)
203 {
204  assert(base);
205 
206  for (int placed = 0; placed < count; placed++) {
207  const int x = rand() % BASE_SIZE;
208  const int y = rand() % BASE_SIZE;
209 
210  if (B_IsTileBlocked(base, x, y))
211  continue;
212 
213  if (B_GetBuildingAt(base, x, y) != nullptr)
214  continue;
215 
216  B_AddBlockedTile(base, y, x);
217  }
218 }
219 
224 bool B_IsBuildingDestroyable (const building_t* building)
225 {
226  assert(building);
227  base_t* base = building->base;
228  assert(base);
229 
230  if (base->baseStatus == BASE_DESTROYED)
231  return true;
232 
233  linkedList_t* queue = nullptr;
234  building_t* bldg = nullptr;
235 
236  while ((bldg = B_GetNextBuilding(base, bldg)) != nullptr) {
237  if (bldg != building) {
238  cgi->LIST_AddPointer(&queue, (void*)bldg);
239  break;
240  }
241  }
242  if (!bldg)
243  return true;
244 
245  int found[MAX_BUILDINGS];
246  OBJZERO(found);
247  /* prevents adding building to be removed to the queue */
248  found[building->idx] = 1;
249 
250  while (!cgi->LIST_IsEmpty(queue)) {
251  bldg = (building_t*)queue->data;
252  found[bldg->idx] = 1;
253  cgi->LIST_RemoveEntry(&queue, queue);
254 
255  linkedList_t* neighbours = B_GetNeighbours(bldg);
256  LIST_Foreach(neighbours, building_t, bldg) {
257  if (found[bldg->idx] == 0) {
258  found[bldg->idx] = 1;
259  cgi->LIST_AddPointer(&queue, (void*)bldg);
260  }
261  }
262  cgi->LIST_Delete(&neighbours);
263  }
264  cgi->LIST_Delete(&queue);
265 
266  for (int i = 0; i < ccs.numBuildings[base->idx]; i++) {
267  if (found[i] != 1)
268  return false;
269  }
270 
271  return true;
272 }
273 
277 int B_GetCount (void)
278 {
279  return ccs.numBases;
280 }
281 
286 base_t* B_GetNext (base_t* lastBase)
287 {
288  base_t* endOfBases = &ccs.bases[B_GetCount()];
289  base_t* base;
290 
291  if (!B_GetCount())
292  return nullptr;
293 
294  if (!lastBase)
295  return ccs.bases;
296  assert(lastBase >= ccs.bases);
297  assert(lastBase < endOfBases);
298 
299  base = lastBase;
300 
301  base++;
302  if (base >= endOfBases)
303  return nullptr;
304  return base;
305 }
306 
313 base_t* B_GetBaseByIDX (int baseIdx)
314 {
315  if (baseIdx >= MAX_BASES || baseIdx < 0)
316  return nullptr;
317 
318  return &ccs.bases[baseIdx];
319 }
320 
327 {
328  if (baseIdx >= B_GetCount())
329  return nullptr;
330 
331  return B_GetBaseByIDX(baseIdx);
332 }
333 
339 building_t* B_GetNextBuilding (const base_t* base, building_t* lastBuilding)
340 {
341  building_t* endOfBuildings = B_GetBuildingByIDX(base->idx, ccs.numBuildings[base->idx]);
342  building_t* building;
343 
344  if (!ccs.numBuildings[base->idx])
345  return nullptr;
346 
347  if (!lastBuilding)
348  return ccs.buildings[base->idx];
349  assert(lastBuilding >= ccs.buildings[base->idx]);
350  assert(lastBuilding < endOfBuildings);
351 
352  building = lastBuilding;
353 
354  building++;
355  if (building >= endOfBuildings)
356  return nullptr;
357  return building;
358 }
359 
367 building_t* B_GetNextBuildingByType (const base_t* base, building_t* lastBuilding, buildingType_t buildingType)
368 {
369  building_t* building = lastBuilding;
370 
371  while ((building = B_GetNextBuilding(base, building))) {
372  if (building->buildingType == buildingType)
373  break;
374  }
375  return building;
376 }
377 
390 bool B_CheckBuildingTypeStatus (const base_t* const base, buildingType_t type, buildingStatus_t status, int* cnt)
391 {
392  int cntlocal = 0;
393  building_t* building = nullptr;
394 
395  while ((building = B_GetNextBuildingByType(base, building, type))) {
396  if (building->buildingStatus != status)
397  continue;
398  cntlocal++;
399  /* don't count any further - the caller doesn't want to know the value */
400  if (!cnt)
401  return true;
402  }
403 
404  /* set the cnt pointer if the caller wants to know this value */
405  if (cnt)
406  *cnt = cntlocal;
407 
408  return cntlocal ? true : false;
409 }
410 
417 {
418  switch (type) {
419  case B_LAB:
420  return CAP_LABSPACE;
421  case B_QUARTERS:
422  return CAP_EMPLOYEES;
423  case B_STORAGE:
424  return CAP_ITEMS;
425  case B_WORKSHOP:
426  return CAP_WORKSPACE;
427  case B_HANGAR:
428  return CAP_AIRCRAFT_BIG;
429  case B_ALIEN_CONTAINMENT:
430  return CAP_ALIENS;
431  case B_SMALL_HANGAR:
432  return CAP_AIRCRAFT_SMALL;
433  case B_ANTIMATTER:
434  return CAP_ANTIMATTER;
435  default:
436  return MAX_CAP;
437  }
438 }
439 
448 {
449  switch (cap) {
450  case CAP_ALIENS:
451  return B_ALIEN_CONTAINMENT;
452  case CAP_AIRCRAFT_SMALL:
453  return B_SMALL_HANGAR;
454  case CAP_AIRCRAFT_BIG:
455  return B_HANGAR;
456  case CAP_EMPLOYEES:
457  return B_QUARTERS;
458  case CAP_ITEMS:
459  return B_STORAGE;
460  case CAP_LABSPACE:
461  return B_LAB;
462  case CAP_WORKSPACE:
463  return B_WORKSHOP;
464  case CAP_ANTIMATTER:
465  return B_ANTIMATTER;
466  default:
467  return MAX_BUILDING_TYPE;
468  }
469 }
470 
478 bool B_GetBuildingStatus (const base_t* const base, const buildingType_t buildingType)
479 {
480  assert(base);
481 
482  if (buildingType == B_MISC)
483  return true;
484 
485  if (buildingType < MAX_BUILDING_TYPE)
486  return base->hasBuilding[buildingType];
487 
488  cgi->Com_Printf("B_GetBuildingStatus: Building-type %i does not exist.\n", buildingType);
489  return false;
490 }
491 
499 void B_SetBuildingStatus (base_t* const base, const buildingType_t buildingType, bool newStatus)
500 {
501  if (buildingType == B_MISC) {
502  cgi->Com_Printf("B_SetBuildingStatus: No status is associated to B_MISC type of building.\n");
503  } else if (buildingType < MAX_BUILDING_TYPE) {
504  assert(base);
505  base->hasBuilding[buildingType] = newStatus;
506  cgi->Com_DPrintf(DEBUG_CLIENT, "B_SetBuildingStatus: set status for %i to %i\n", buildingType, newStatus);
507  } else {
508  cgi->Com_Printf("B_SetBuildingStatus: Type of building %i does not exist\n", buildingType);
509  }
510 }
511 
520 {
521  float max = 0.0f;
522 
523  if (B_GetBuildingStatus(base, type)) {
524  building_t* building = nullptr;
525  while ((building = B_GetNextBuildingByType(base, building, type)))
526  if (building->buildingStatus == B_STATUS_WORKING)
527  max = std::max(building->level, max);
528  }
529 
530  return max;
531 }
532 
545 static inline void B_AddMap (char* maps, size_t mapsLength, char* coords, size_t coordsLength, const char* map, int col, int row)
546 {
547  Q_strcat(coords, coordsLength, "%i %i %i ", col * BASE_TILE_UNITS, (BASE_SIZE - row - 1) * BASE_TILE_UNITS, 0);
548  Q_strcat(maps, mapsLength, "%s", map);
549 }
550 
563 bool B_AssembleMap (char* maps, size_t mapsLength, char* coords, size_t coordsLength, const base_t* base)
564 {
565  if (!base) {
566  cgi->Com_Printf("B_AssembleMap: No base to assemble\n");
567  return false;
568  }
569 
570  maps[0] = '\0';
571  coords[0] = '\0';
572 
573  for (int row = 0; row < BASE_SIZE; row++) {
574  for (int col = 0; col < BASE_SIZE; col++) {
575  const building_t* building = B_GetBuildingAt(base, col, row);
576  if (!building) {
577  B_AddMap(maps, mapsLength, coords, coordsLength, "b/empty ", col, row);
578  continue;
579  }
580  if (building->pos[0] != col || building->pos[1] != row)
581  continue;
582  if (!B_IsBuildingBuiltUp(building)) {
583  B_AddMap(maps, mapsLength, coords, coordsLength, "b/construction ", col, row);
584  continue;
585  }
586  if (!building->mapPart)
587  cgi->Com_Error(ERR_DROP, "MapPart for building '%s' is missing'", building->id);
588  B_AddMap(maps, mapsLength, coords, coordsLength, va("b/%s ", building->mapPart), col, row);
589  }
590  }
591 
592  return true;
593 }
594 
600 static bool B_CheckUpdateBuilding (building_t* building)
601 {
602  bool oldValue;
603 
604  assert(building);
605 
606  /* Status of Miscellenious buildings cannot change. */
607  if (building->buildingType == B_MISC)
608  return false;
609 
610  oldValue = B_GetBuildingStatus(building->base, building->buildingType);
611  if (building->buildingStatus == B_STATUS_WORKING
613  B_SetBuildingStatus(building->base, building->buildingType, true);
614  else
615  B_SetBuildingStatus(building->base, building->buildingType, false);
616 
617  if (B_GetBuildingStatus(building->base, building->buildingType) != oldValue) {
618  cgi->Com_DPrintf(DEBUG_CLIENT, "Status of building %s is changed to %i.\n",
619  building->name, B_GetBuildingStatus(building->base, building->buildingType));
620  return true;
621  }
622 
623  return false;
624 }
625 
635 static bool B_UpdateStatusBuilding (base_t* base, buildingType_t buildingType, bool onBuilt)
636 {
637  bool test = false;
638  bool returnValue = false;
639  building_t* building = nullptr;
640 
641  /* Construction / destruction may have changed the status of other building
642  * We check that, but only for buildings which needed building */
643  while ((building = B_GetNextBuilding(base, building))) {
644  const building_t* dependsBuilding = building->dependsBuilding;
645  if (dependsBuilding && buildingType == dependsBuilding->buildingType) {
646  /* ccs.buildings[base->idx][i] needs built/removed building */
647  if (onBuilt && !B_GetBuildingStatus(base, building->buildingType)) {
648  /* we can only activate a non operational building */
649  if (B_CheckUpdateBuilding(building)) {
650  B_FireEvent(building, base, B_ONENABLE);
651  test = true;
652  returnValue = true;
653  }
654  } else if (!onBuilt && B_GetBuildingStatus(base, building->buildingType)) {
655  /* we can only deactivate an operational building */
656  if (B_CheckUpdateBuilding(building)) {
657  B_FireEvent(building, base, B_ONDISABLE);
658  test = true;
659  returnValue = true;
660  }
661  }
662  }
663  }
664  /* and maybe some updated status have changed status of other building.
665  * So we check again, until nothing changes. (no condition here for check, it's too complex) */
666  while (test) {
667  test = false;
668  building = nullptr;
669  while ((building = B_GetNextBuilding(base, building))) {
670  if (onBuilt && !B_GetBuildingStatus(base, building->buildingType)) {
671  /* we can only activate a non operational building */
672  if (B_CheckUpdateBuilding(building)) {
673  B_FireEvent(building, base, B_ONENABLE);
674  test = true;
675  }
676  } else if (!onBuilt && B_GetBuildingStatus(base, building->buildingType)) {
677  /* we can only deactivate an operational building */
678  if (B_CheckUpdateBuilding(building)) {
679  B_FireEvent(building, base, B_ONDISABLE);
680  test = true;
681  }
682  }
683  }
684  }
685 
686  return returnValue;
687 }
688 
694 static void B_UpdateAntimatterCap (base_t* base)
695 {
697  if (od != nullptr)
698  CAP_SetCurrent(base, CAP_ANTIMATTER, B_ItemInBase(od, base));
699 }
700 
706 void B_ResetAllStatusAndCapacities (base_t* base, bool firstEnable)
707 {
708  bool test = true;
709 
710  assert(base);
711 
712  cgi->Com_DPrintf(DEBUG_CLIENT, "Reseting base %s:\n", base->name);
713 
714  /* reset all values of hasBuilding[] */
715  for (int i = 0; i < MAX_BUILDING_TYPE; i++) {
717  if (type != B_MISC)
718  B_SetBuildingStatus(base, type, false);
719  }
720  /* activate all buildings that needs to be activated */
721  while (test) {
722  building_t* building = nullptr;
723  test = false;
724  while ((building = B_GetNextBuilding(base, building))) {
725  if (!B_GetBuildingStatus(base, building->buildingType)
726  && B_CheckUpdateBuilding(building)) {
727  if (firstEnable)
728  B_FireEvent(building, base, B_ONENABLE);
729  test = true;
730  }
731  }
732  }
733 
734  /* Update all capacities of base */
736 
737  /* calculate capacities.cur for every capacity */
738 
739  /* Current CAP_ALIENS (live alien capacity) is managed by AlienContainment class */
740  /* Current aircraft capacities should not need recounting */
741 
743  CAP_SetCurrent(base, CAP_EMPLOYEES, E_CountAllHired(base, true));
744 
746  CAP_UpdateStorageCap(base);
747 
750 
753 
755  B_UpdateAntimatterCap(base);
756 
757  /* Check that current capacity is possible -- if we changed values in *.ufo */
758  for (int i = 0; i < MAX_CAP; i++) {
759  const baseCapacities_t cap = (baseCapacities_t)i;
760  if (CAP_GetFreeCapacity(base, cap) < 0)
761  cgi->Com_Printf("B_ResetAllStatusAndCapacities: Warning, capacity of %i is bigger than maximum capacity\n", i);
762  }
763 }
764 
772 {
773  /* Don't allow to destroy a mandatory building. */
774  if (building->mandatory)
775  return false;
776 
777  base_t* base = building->base;
778 
779  if (base->map[(int)building->pos[1]][(int)building->pos[0]].building != building) {
780  cgi->Com_Error(ERR_DROP, "B_BuildingDestroy: building mismatch at base %i pos %i,%i.",
781  base->idx, (int)building->pos[0], (int)building->pos[1]);
782  }
783 
784  /* Refuse destroying if it hurts coherency - only exception is when the whole base destroys */
785  if (!B_IsBuildingDestroyable(building)) {
786  return false;
787  }
788 
789  /* liquidation rate gives back percentage of the worth of a building when it's recycled. Making replacing buildings more practical */
791 
792  const buildingType_t buildingType = building->buildingType;
793  const building_t* buildingTemplate = building->tpl;
794  const bool runDisableCommand = building->buildingStatus == B_STATUS_WORKING;
795  building->buildingStatus = B_STATUS_NOT_SET;
796 
797  int const baseIDX = base->idx;
798  building_t* const buildings = ccs.buildings[baseIDX];
799  int const idx = building->idx;
800 
801  for (int y = building->pos[1]; y < building->pos[1] + building->size[1]; y++)
802  for (int x = building->pos[0]; x < building->pos[0] + building->size[0]; x++)
803  base->map[y][x].building = nullptr;
804 
805  REMOVE_ELEM(buildings, idx, ccs.numBuildings[baseIDX]);
806 
807  /* Update the link of other buildings */
808  const int cntBldgs = ccs.numBuildings[baseIDX];
809  for (int i = 0; i < cntBldgs; i++) {
810  building_t* bldg = &buildings[i];
811  if (bldg->idx < idx)
812  continue;
813  bldg->idx--;
814 
815  for (int y = bldg->pos[1]; y < bldg->pos[1] + bldg->size[1]; y++)
816  for (int x = (int)bldg->pos[0]; x < bldg->pos[0] + bldg->size[0]; x++)
817  base->map[y][x].building = bldg;
818  }
819  building = nullptr;
820 
821  /* Don't use the building pointer after this point - it's zeroed. */
822 
823  if (buildingType != B_MISC && buildingType != MAX_BUILDING_TYPE) {
824  if (B_GetNumberOfBuildingsInBaseByBuildingType(base, buildingType) <= 0) {
825  /* there is no more building of this type */
826  B_SetBuildingStatus(base, buildingType, false);
827  /* check if this has an impact on other buildings */
828  B_UpdateStatusBuilding(base, buildingType, false);
829  /* we may have changed status of several building: update all capacities */
831  } else {
832  /* there is still at least one other building of the same type in base: just update capacity */
833  const baseCapacities_t cap = B_GetCapacityFromBuildingType(buildingType);
834  if (cap != MAX_CAP)
835  B_UpdateBaseCapacities(cap, base);
836  }
837  }
838 
839  /* call ondisable trigger only if building is not under construction
840  * (we do that after base capacity has been updated) */
841  if (runDisableCommand) {
842  if (B_FireEvent(buildingTemplate, base, B_ONDISABLE))
843  cgi->Com_DPrintf(DEBUG_CLIENT, "B_BuildingDestroy: %s %i %i;\n", buildingTemplate->onDisable, base->idx, buildingType);
844  }
845  if (B_FireEvent(buildingTemplate, base, B_ONDESTROY))
846  cgi->Com_DPrintf(DEBUG_CLIENT, "B_BuildingDestroy: %s %i %i;\n", buildingTemplate->onDestroy, base->idx, buildingType);
847 
849 
850  cgi->Cmd_ExecuteString("base_init %d", base->idx);
851 
852  return true;
853 }
854 
862 {
863  AIR_ForeachFromBase(aircraft, base) {
864  if (!AIR_IsAircraftOnGeoscape(aircraft))
865  continue;
866  base_t* newbase = nullptr;
867  bool moved = false;
868  while ((newbase = B_GetNext(newbase)) != nullptr) {
869  /* found a new homebase? */
870  if (base != newbase && !AIR_CheckMoveIntoNewHomebase(aircraft, newbase)) {
871  AIR_MoveAircraftIntoNewHomebase(aircraft, newbase);
872  moved = true;
873  break;
874  }
875  }
876 
877  if (moved)
878  continue;
879 
880  /* No base can hold this aircraft */
882  if (!MapIsWater(GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr))) {
883  CP_SpawnRescueMission(aircraft, nullptr);
884  } else {
885  /* Destroy the aircraft and everything onboard - the aircraft pointer
886  * is no longer valid after this point */
888  AIR_DestroyAircraft(aircraft);
889  }
890  }
891 }
892 
898 void B_Delete (base_t* base)
899 {
900  assert(base);
901 
902  if (base->alienContainment != nullptr) {
903  delete base->alienContainment;
904  base->alienContainment = nullptr;
905  }
906 }
907 
914 void B_Destroy (base_t* base)
915 {
916  assert(base);
917  base->baseStatus = BASE_DESTROYED;
918 
921 
922  /* do a reverse loop as buildings are going to be destroyed */
923  for (int buildingIdx = ccs.numBuildings[base->idx] - 1; buildingIdx >= 0; buildingIdx--) {
924  building_t* building = B_GetBuildingByIDX(base->idx, buildingIdx);
925  B_BuildingDestroy(building);
926  }
927 
928  E_DeleteAllEmployees(base);
929 
930  AIR_ForeachFromBase(aircraft, base) {
931  AIR_DeleteAircraft(aircraft);
932  }
933 
934  OBJZERO(base->storage);
935  CAP_SetCurrent(base, CAP_ITEMS, 0);
936 
937  base->alienInterest = 0;
938 
942  B_Delete(base);
943 }
944 
945 #ifdef DEBUG
946 
949 static void B_Destroy_f (void)
950 {
951  if (cgi->Cmd_Argc() < 2) {
952  cgi->Com_Printf("Usage: %s <baseIdx>\n", cgi->Cmd_Argv(0));
953  return;
954  }
955 
956  const int baseIdx = atoi(cgi->Cmd_Argv(1));
957  if (baseIdx < 0 || baseIdx >= MAX_BASES) {
958  cgi->Com_Printf("B_Destroy_f: baseIdx %i is outside bounds\n", baseIdx);
959  return;
960  }
961 
962  base_t* base = B_GetFoundedBaseByIDX(baseIdx);
963  if (!base) {
964  cgi->Com_Printf("B_Destroy_f: Base %i not founded\n", baseIdx);
965  return;
966  }
967 
968  B_Destroy(base);
969 }
970 #endif
971 
980 {
981  building->buildingStatus = status;
982 
983  /* we update the status of the building (we'll call this building building 1) */
984  const bool test = B_CheckUpdateBuilding(building);
985  if (test) {
986  base_t* base = building->base;
987  B_FireEvent(building, base, B_ONENABLE);
988  /* now, the status of this building may have changed the status of other building.
989  * We check that, but only for buildings which needed building 1 */
990  B_UpdateStatusBuilding(base, building->buildingType, true);
991  /* we may have changed status of several building: update all capacities */
993  } else {
994  /* no other status than status of building 1 has been modified
995  * update only status of building 1 */
997  if (cap != MAX_CAP) {
998  base_t* base = building->base;
999  B_UpdateBaseCapacities(cap, base);
1000  }
1001  }
1002 }
1003 
1011 static void B_AddBuildingToBasePos (base_t* base, const building_t* buildingTemplate, bool hire, const vec2_t pos)
1012 {
1013  /* new building in base (not a template) */
1014  building_t* buildingNew;
1015 
1016  buildingNew = B_BuildBuilding(base, buildingTemplate, (int)pos[0], (int)pos[1]);
1017  if (!buildingNew)
1018  return;
1019  buildingNew->timeStart = DateTime(0, 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 = DateTime(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("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 = DateTime(0, 0);
1927  B_FireEvent(building, base, B_ONENABLE);
1928  }
1929  /* update menu */
1930  B_SelectBase(base);
1931 }
1932 
1937 static void B_ResetAllStatusAndCapacities_f (void)
1938 {
1939  base_t* base;
1940 
1941  base = nullptr;
1942  while ((base = B_GetNext(base)) != nullptr) {
1943  /* set buildingStatus[] and capacities.max values */
1944  B_ResetAllStatusAndCapacities(base, false);
1945  }
1946 }
1947 
1951 static void B_CheckCoherency_f (void)
1952 {
1953  if (cgi->Cmd_Argc() < 2) {
1954  cgi->Com_Printf("Usage: %s <baseIDX>\n", cgi->Cmd_Argv(0));
1955  return;
1956  }
1957  base_t* base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(1)));
1958  if (!base) {
1959  cgi->Com_Printf("Invalid base idx: %s\n", cgi->Cmd_Argv(1));
1960  return;
1961  }
1962 
1963  cgi->Com_Printf("Base '%s' (idx:%i) is %scoherent.\n", base->name, base->idx, (B_IsCoherent(base)) ? "" : "not ");
1964 }
1965 #endif
1966 
1970 void B_InitStartup (void)
1971 {
1972 #ifdef DEBUG
1973  cgi->Cmd_AddCommand("debug_listbase", B_BaseList_f, "Print base information to the game console");
1974  cgi->Cmd_AddCommand("debug_listbuilding", B_BuildingList_f, "Print building information to the game console");
1975  cgi->Cmd_AddCommand("debug_listcapacities", B_PrintCapacities_f, "Debug function to show all capacities in given base");
1976  cgi->Cmd_AddCommand("debug_basereset", B_ResetAllStatusAndCapacities_f, "Reset building status and capacities of all bases");
1977  cgi->Cmd_AddCommand("debug_destroybase", B_Destroy_f, "Destroy a base");
1978  cgi->Cmd_AddCommand("debug_buildingfinished", B_BuildingConstructionFinished_f, "Finish construction for every building in the current base");
1979  cgi->Cmd_AddCommand("debug_baseiscoherent", B_CheckCoherency_f, "Checks if all buildings are connected on a base");
1980 #endif
1981 }
1982 
1988 {
1990  return false;
1991 
1992  if (!B_IsBuildingBuiltUp(building))
1993  return false;
1994 
1995  base_t* base = building->base;
1996 
1998  if (B_FireEvent(building, base, B_ONENABLE))
1999  cgi->Com_DPrintf(DEBUG_CLIENT, "B_CheckBuildingConstruction: %s %i;\n", building->onEnable, base->idx);
2000 
2001  return true;
2002 }
2003 
2010 {
2011  base_t* base = nullptr;
2012  while ((base = B_GetNext(base)) != nullptr) {
2013  building_t* building = nullptr;
2014  while ((building = B_GetNextBuilding(base, building))) {
2015  if (!B_CheckBuildingConstruction(building))
2016  continue;
2017 
2019  _("Construction of %s building finished in %s."), _(building->name), base->name);
2021  }
2022  }
2023 }
2024 
2030 static void B_SellOrAddItems (aircraft_t* aircraft)
2031 {
2032  int numitems = 0;
2033  int gained = 0;
2034 
2035  assert(aircraft);
2036  base_t* base = aircraft->homebase;
2037  assert(base);
2038 
2039  if (aircraft->itemCargo == nullptr)
2040  return;
2041 
2042  linkedList_t* items = aircraft->itemCargo->list();
2043  LIST_Foreach(items, itemCargo_t, item) {
2044  technology_t* const tech = RS_GetTechForItem(item->objDef);
2045 
2046  if (!RS_IsResearched_ptr(tech)) {
2047  /* Items not researched cannot be thrown out even if not enough space in storage. */
2048  B_AddToStorage(base, item->objDef, item->amount);
2049  if (item->amount > 0)
2050  RS_MarkCollected(tech);
2051  continue;
2052  } else {
2053  /* If the related technology is researched, check the autosell option. */
2054  if (ccs.eMarket.autosell[item->objDef->idx]) { /* Sell items if autosell is enabled. */
2055  BS_SellItem(item->objDef, nullptr, item->amount);
2056  gained += BS_GetItemSellingPrice(item->objDef) * item->amount;
2057  numitems += item->amount;
2058  } else {
2059  B_AddToStorage(base, item->objDef, item->amount);
2060  }
2061  continue;
2062  }
2063  }
2064  cgi->LIST_Delete(&items);
2065 
2066  if (numitems > 0) {
2067  Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("By selling %s you gathered %i credits."),
2068  va(ngettext("%i collected item", "%i collected items", numitems), numitems), gained);
2069  MS_AddNewMessage(_("Notice"), cp_messageBuffer);
2070  }
2071 
2072  aircraft->itemCargo->empty();
2073 
2074  /* Mark new technologies researchable. */
2075  RS_MarkResearchable(aircraft->homebase);
2076  /* Recalculate storage capacity, to fix wrong capacity if a soldier drops something on the ground */
2077  CAP_UpdateStorageCap(aircraft->homebase);
2078 }
2079 
2085 {
2086  /* Don't call cargo functions if aircraft is not a transporter. */
2087  if (aircraft->maxTeamSize <= 0)
2088  return;
2089 
2090  /* Add aliens to Alien Containment. */
2091  AL_AddAliens(aircraft);
2092  /* Sell collected items or add them to storage. */
2093  B_SellOrAddItems(aircraft);
2094 }
2095 
2103 {
2104  /* AA Missiles should miss */
2106  /* Reset UFO sensored on radar */
2107  RADAR_InitialiseUFOs(&aircraft->radar);
2108  /* Reload weapons */
2109  AII_ReloadAircraftWeapons(aircraft);
2110 
2111  B_DumpAircraftToHomeBase(aircraft);
2112 }
2113 
2119 bool B_BaseHasItem (const base_t* base, const objDef_t* item)
2120 {
2121  if (item->isVirtual)
2122  return true;
2123 
2124  return B_ItemInBase(item, base) > 0;
2125 }
2126 
2133 int B_ItemInBase (const objDef_t* item, const base_t* base)
2134 {
2135  const equipDef_t* ed;
2136 
2137  if (!item)
2138  return -1;
2139  if (item->isVirtual)
2140  return -1;
2141  if (!base)
2142  return -1;
2143 
2144  ed = &base->storage;
2145 
2146  if (!ed)
2147  return -1;
2148 
2149  return ed->numItems[item->idx];
2150 }
2151 
2161 {
2162  switch (cap) {
2163  case CAP_ALIENS:
2164  case CAP_EMPLOYEES:
2165  case CAP_LABSPACE:
2166  case CAP_WORKSPACE:
2167  case CAP_ITEMS:
2168  case CAP_AIRCRAFT_SMALL:
2169  case CAP_AIRCRAFT_BIG:
2170  case CAP_ANTIMATTER:
2171  {
2172  int buildingTemplateIDX = -1;
2173  const buildingType_t buildingType = B_GetBuildingTypeByCapacity(cap);
2174  int capacity = 0;
2175 
2176  /* Reset given capacity in current base. */
2177  CAP_SetMax(base, cap, 0);
2178  /* Get building capacity. */
2179  for (int i = 0; i < ccs.numBuildingTemplates; i++) {
2180  const building_t* b = &ccs.buildingTemplates[i];
2181  if (b->buildingType == buildingType) {
2182  capacity = b->capacity;
2183  cgi->Com_DPrintf(DEBUG_CLIENT, "Building: %s capacity: %i\n", b->id, capacity);
2184  buildingTemplateIDX = i;
2185  break;
2186  }
2187  }
2188  /* Finally update capacity. */
2189  building_t* building = nullptr;
2190  while ((building = B_GetNextBuildingByType(base, building, buildingType)))
2192  CAP_AddMax(base, cap, capacity);
2193 
2194  if (buildingTemplateIDX != -1)
2195  cgi->Com_DPrintf(DEBUG_CLIENT, "B_UpdateBaseCapacities: updated capacity of %s: %i\n",
2196  ccs.buildingTemplates[buildingTemplateIDX].id, CAP_GetMax(base, cap));
2197 
2198  if (cap == CAP_ALIENS) {
2199  const capacities_t* alienCap = CAP_Get(base, CAP_ALIENS);
2200  if (base->alienContainment != nullptr && alienCap->max == 0 && alienCap->cur == 0) {
2201  delete base->alienContainment;
2202  base->alienContainment = nullptr;
2203  } else if (base->alienContainment == nullptr && alienCap->max > 0) {
2204  base->alienContainment = new AlienContainment(CAP_Get(base, CAP_ALIENS), nullptr);
2205  }
2206  }
2207  break;
2208  }
2209  case MAX_CAP:
2210  cgi->Com_DPrintf(DEBUG_CLIENT, "B_UpdateBaseCapacities: going to update ALL capacities.\n");
2211  /* Loop through all capacities and update them. */
2212  for (int i = 0; i < cap; i++) {
2213  const baseCapacities_t cap = (baseCapacities_t) i;
2214  B_UpdateBaseCapacities(cap, base);
2215  }
2216  break;
2217  default:
2218  cgi->Com_Error(ERR_DROP, "Unknown capacity limit for this base: %i \n", cap);
2219  }
2220 }
2221 
2228 void B_SaveBaseSlotsXML (const baseWeapon_t* weapons, const int numWeapons, xmlNode_t* node)
2229 {
2230  for (int i = 0; i < numWeapons; i++) {
2231  xmlNode_t* sub = cgi->XML_AddNode(node, SAVE_BASES_WEAPON);
2232  AII_SaveOneSlotXML(sub, &weapons[i].slot, true);
2233  cgi->XML_AddBool(sub, SAVE_BASES_AUTOFIRE, weapons[i].autofire);
2234  if (weapons[i].target)
2235  cgi->XML_AddInt(sub, SAVE_BASES_TARGET, weapons[i].target->idx);
2236  }
2237 }
2238 
2244 bool B_SaveStorageXML (xmlNode_t* parent, const equipDef_t& equip)
2245 {
2246  for (int k = 0; k < cgi->csi->numODs; k++) {
2247  const objDef_t* od = INVSH_GetItemByIDX(k);
2248  if (equip.numItems[k] || equip.numItemsLoose[k]) {
2249  xmlNode_t* node = cgi->XML_AddNode(parent, SAVE_BASES_ITEM);
2250 
2251  cgi->XML_AddString(node, SAVE_BASES_ODS_ID, od->id);
2252  cgi->XML_AddIntValue(node, SAVE_BASES_NUM, equip.numItems[k]);
2253  cgi->XML_AddByteValue(node, SAVE_BASES_NUMLOOSE, equip.numItemsLoose[k]);
2254  }
2255  }
2256  return true;
2257 }
2258 
2263 bool B_SaveXML (xmlNode_t* parent)
2264 {
2265  xmlNode_t* bases;
2266  base_t* b;
2267 
2268  bases = cgi->XML_AddNode(parent, SAVE_BASES_BASES);
2269  b = nullptr;
2270  while ((b = B_GetNext(b)) != nullptr) {
2271 
2272  if (!b->founded) {
2273  cgi->Com_Printf("B_SaveXML: Base (idx: %i) not founded!\n", b->idx);
2274  return false;
2275  }
2276 
2277  cgi->Com_RegisterConstList(saveBaseConstants);
2278 
2279  xmlNode_t* act_base = cgi->XML_AddNode(bases, SAVE_BASES_BASE);
2280  cgi->XML_AddInt(act_base, SAVE_BASES_IDX, b->idx);
2281  cgi->XML_AddString(act_base, SAVE_BASES_NAME, b->name);
2282  cgi->XML_AddPos3(act_base, SAVE_BASES_POS, b->pos);
2284  cgi->XML_AddFloat(act_base, SAVE_BASES_ALIENINTEREST, b->alienInterest);
2285 
2286  /* building space */
2287  xmlNode_t* node = cgi->XML_AddNode(act_base, SAVE_BASES_BUILDINGSPACE);
2288  for (int row = 0; row < BASE_SIZE; row++) {
2289  for (int column = 0; column < BASE_SIZE; column++) {
2290  xmlNode_t* snode = cgi->XML_AddNode(node, SAVE_BASES_BUILDING);
2292  cgi->XML_AddInt(snode, SAVE_BASES_X, row);
2293  cgi->XML_AddInt(snode, SAVE_BASES_Y, column);
2294  if (B_GetBuildingAt(b, column, row))
2295  cgi->XML_AddInt(snode, SAVE_BASES_BUILDINGINDEX, B_GetBuildingAt(b, column, row)->idx);
2296  cgi->XML_AddBoolValue(snode, SAVE_BASES_BLOCKED, B_IsTileBlocked(b, column, row));
2297  }
2298  }
2299  /* buildings */
2300  node = cgi->XML_AddNode(act_base, SAVE_BASES_BUILDINGS);
2301  building_t* building = nullptr;
2302  while ((building = B_GetNextBuilding(b, building))) {
2303  xmlNode_t* snode;
2304 
2305  if (!building->tpl)
2306  continue;
2307 
2308  snode = cgi->XML_AddNode(node, SAVE_BASES_BUILDING);
2309  cgi->XML_AddString(snode, SAVE_BASES_BUILDINGTYPE, building->tpl->id);
2310  cgi->XML_AddInt(snode, SAVE_BASES_BUILDING_PLACE, building->idx);
2312  cgi->XML_AddDate(snode, SAVE_BASES_BUILDINGTIMESTART, building->timeStart.getDateAsDays(), building->timeStart.getTimeAsSeconds());
2313  cgi->XML_AddInt(snode, SAVE_BASES_BUILDINGBUILDTIME, building->buildTime);
2314  cgi->XML_AddFloatValue(snode, SAVE_BASES_BUILDINGLEVEL, building->level);
2315  cgi->XML_AddPos2(snode, SAVE_BASES_POS, building->pos);
2316  }
2317  /* base defences */
2318  node = cgi->XML_AddNode(act_base, SAVE_BASES_BATTERIES);
2320  node = cgi->XML_AddNode(act_base, SAVE_BASES_LASERS);
2321  B_SaveBaseSlotsXML(b->lasers, b->numLasers, node);
2322  /* store equipment */
2323  node = cgi->XML_AddNode(act_base, SAVE_BASES_STORAGE);
2324  B_SaveStorageXML(node, b->storage);
2325  /* radar */
2326  cgi->XML_AddIntValue(act_base, SAVE_BASES_RADARRANGE, b->radar.range);
2327  cgi->XML_AddIntValue(act_base, SAVE_BASES_TRACKINGRANGE, b->radar.trackingRange);
2328  /* alien containment */
2329  if (b->alienContainment) {
2330  node = cgi->XML_AddNode(act_base, SAVE_BASES_ALIENCONTAINMENT);
2331  b->alienContainment->save(node);
2332  }
2333 
2334  cgi->Com_UnregisterConstList(saveBaseConstants);
2335  }
2336  return true;
2337 }
2338 
2347 int B_LoadBaseSlotsXML (baseWeapon_t* weapons, int max, xmlNode_t* p)
2348 {
2349  int i;
2350  xmlNode_t* s;
2351  for (i = 0, s = cgi->XML_GetNode(p, SAVE_BASES_WEAPON); s && i < max; i++, s = cgi->XML_GetNextNode(s, p, SAVE_BASES_WEAPON)) {
2352  const int target = cgi->XML_GetInt(s, SAVE_BASES_TARGET, -1);
2353  AII_LoadOneSlotXML(s, &weapons[i].slot, true);
2354  weapons[i].autofire = cgi->XML_GetBool(s, SAVE_BASES_AUTOFIRE, true);
2355  weapons[i].target = (target >= 0) ? UFO_GetByIDX(target) : nullptr;
2356  }
2357  return i;
2358 }
2359 
2364 static bool B_PostLoadInitCapacity (void)
2365 {
2366  base_t* base = nullptr;
2367  while ((base = B_GetNext(base)) != nullptr)
2368  B_ResetAllStatusAndCapacities(base, true);
2369 
2370  return true;
2371 }
2372 
2377 bool B_PostLoadInit (void)
2378 {
2379  return B_PostLoadInitCapacity();
2380 }
2381 
2387 bool B_LoadStorageXML (xmlNode_t* parent, equipDef_t* equip)
2388 {
2389  for (xmlNode_t* node = cgi->XML_GetNode(parent, SAVE_BASES_ITEM); node; node = cgi->XML_GetNextNode(node, parent, SAVE_BASES_ITEM)) {
2390  const char* s = cgi->XML_GetString(node, SAVE_BASES_ODS_ID);
2391  const objDef_t* od = INVSH_GetItemByID(s);
2392  if (!od) {
2393  cgi->Com_Printf("B_Load: Could not find item '%s'\n", s);
2394  continue;
2395  }
2396  equip->numItems[od->idx] = cgi->XML_GetInt(node, SAVE_BASES_NUM, 0);
2397  equip->numItemsLoose[od->idx] = cgi->XML_GetInt(node, SAVE_BASES_NUMLOOSE, 0);
2398  }
2399  return true;
2400 }
2401 
2406 bool B_LoadXML (xmlNode_t* parent)
2407 {
2408  int buildingIdx;
2409  xmlNode_t* bases;
2410 
2411  bases = cgi->XML_GetNode(parent, "bases");
2412  if (!bases) {
2413  cgi->Com_Printf("Error: Node 'bases' wasn't found in savegame\n");
2414  return false;
2415  }
2416 
2417  ccs.numBases = 0;
2418 
2419  cgi->Com_RegisterConstList(saveBaseConstants);
2420  FOREACH_XMLNODE(base, bases, SAVE_BASES_BASE) {
2421  const int i = ccs.numBases;
2422  base_t* const b = B_GetBaseByIDX(i);
2423  if (b == nullptr)
2424  break;
2425 
2426  ccs.numBases++;
2427 
2428  b->idx = cgi->XML_GetInt(base, SAVE_BASES_IDX, -1);
2429  if (b->idx < 0) {
2430  cgi->Com_Printf("Invalid base index %i\n", b->idx);
2431  cgi->Com_UnregisterConstList(saveBaseConstants);
2432  return false;
2433  }
2434  b->founded = true;
2435  const char* str = cgi->XML_GetString(base, SAVE_BASES_BASESTATUS);
2436  if (!cgi->Com_GetConstIntFromNamespace(SAVE_BASESTATUS_NAMESPACE, str, (int*) &b->baseStatus)) {
2437  cgi->Com_Printf("Invalid base status '%s'\n", str);
2438  cgi->Com_UnregisterConstList(saveBaseConstants);
2439  return false;
2440  }
2441 
2442  Q_strncpyz(b->name, cgi->XML_GetString(base, SAVE_BASES_NAME), sizeof(b->name));
2443  cgi->XML_GetPos3(base, SAVE_BASES_POS, b->pos);
2444  b->alienInterest = cgi->XML_GetFloat(base, SAVE_BASES_ALIENINTEREST, 0.0);
2445  b->aircraftCurrent = nullptr;
2446 
2447  /* building space */
2449  FOREACH_XMLNODE(snode, node, SAVE_BASES_BUILDING) {
2451  const int x = cgi->XML_GetInt(snode, SAVE_BASES_X, 0);
2452  const int y = cgi->XML_GetInt(snode, SAVE_BASES_Y, 0);
2453  baseBuildingTile_t* tile = &b->map[x][y];
2454  buildingIdx = cgi->XML_GetInt(snode, SAVE_BASES_BUILDINGINDEX, -1);
2455 
2456  tile->posX = y; /* NOT a typo ! */
2457  tile->posY = x;
2458  if (buildingIdx != -1)
2459  /* The buildings are actually parsed _below_. (See PRE_MAXBUI loop) */
2460  tile->building = B_GetBuildingByIDX(i, buildingIdx);
2461  else
2462  tile->building = nullptr;
2463  tile->blocked = cgi->XML_GetBool(snode, SAVE_BASES_BLOCKED, false);
2464  if (tile->blocked && tile->building != nullptr) {
2465  cgi->Com_Printf("inconstent base layout found\n");
2466  cgi->Com_UnregisterConstList(saveBaseConstants);
2467  return false;
2468  }
2469  }
2470  /* buildings */
2471  node = cgi->XML_GetNode(base, SAVE_BASES_BUILDINGS);
2472 
2473  ccs.numBuildings[i] = 0;
2474  FOREACH_XMLNODE(snode, node, SAVE_BASES_BUILDING) {
2475  const int buildId = cgi->XML_GetInt(snode, SAVE_BASES_BUILDING_PLACE, MAX_BUILDINGS);
2476  building_t* building;
2477  const building_t* buildingTemplate;
2478  char buildingType[MAX_VAR];
2479 
2480  if (buildId >= MAX_BUILDINGS) {
2481  cgi->Com_Printf("building ID is greater than MAX buildings\n");
2482  cgi->Com_UnregisterConstList(saveBaseConstants);
2483  return false;
2484  }
2485 
2486  Q_strncpyz(buildingType, cgi->XML_GetString(snode, SAVE_BASES_BUILDINGTYPE), sizeof(buildingType));
2487  if (buildingType[0] == '\0') {
2488  cgi->Com_Printf("No buildingtype set\n");
2489  cgi->Com_UnregisterConstList(saveBaseConstants);
2490  return false;
2491  }
2492 
2493  buildingTemplate = B_GetBuildingTemplate(buildingType);
2494  if (!buildingTemplate)
2495  continue;
2496 
2497  ccs.buildings[i][buildId] = *buildingTemplate;
2498  building = B_GetBuildingByIDX(i, buildId);
2499  building->idx = B_GetBuildingIDX(b, building);
2500  if (building->idx != buildId) {
2501  cgi->Com_Printf("building ID doesn't match\n");
2502  cgi->Com_UnregisterConstList(saveBaseConstants);
2503  return false;
2504  }
2505  building->base = b;
2506 
2508  if (!cgi->Com_GetConstIntFromNamespace(SAVE_BUILDINGSTATUS_NAMESPACE, str, (int*) &building->buildingStatus)) {
2509  cgi->Com_Printf("Invalid building status '%s'\n", str);
2510  cgi->Com_UnregisterConstList(saveBaseConstants);
2511  return false;
2512  }
2513 
2514  int date;
2515  int time;
2516  cgi->XML_GetDate(snode, SAVE_BASES_BUILDINGTIMESTART, &date, &time);
2517  building->timeStart = DateTime(date, time);
2518 
2519  building->buildTime = cgi->XML_GetInt(snode, SAVE_BASES_BUILDINGBUILDTIME, 0);
2520  building->level = cgi->XML_GetFloat(snode, SAVE_BASES_BUILDINGLEVEL, 0);
2521  cgi->XML_GetPos2(snode, SAVE_BASES_POS, building->pos);
2522  ccs.numBuildings[i]++;
2523  }
2524 
2526  /* read missile battery slots */
2527  node = cgi->XML_GetNode(base, SAVE_BASES_BATTERIES);
2528  if (node)
2530  /* read laser battery slots */
2531  node = cgi->XML_GetNode(base, SAVE_BASES_LASERS);
2532  if (node)
2534  /* read equipment */
2535  node = cgi->XML_GetNode(base, SAVE_BASES_STORAGE);
2536  B_LoadStorageXML(node, &(b->storage));
2537  /* read radar info */
2539  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);
2540 
2542  if (node) {
2543  b->alienContainment = new AlienContainment(CAP_Get(b, CAP_ALIENS), nullptr);
2544  b->alienContainment->load(node);
2545  }
2546 
2547  /* clear the mess of stray loaded pointers */
2548  b->bEquipment.init();
2549  }
2550  cgi->Com_UnregisterConstList(saveBaseConstants);
2551  cgi->Cvar_SetValue("mn_base_count", B_GetCount());
2552  return true;
2553 }
2554 
2561 {
2562  /* antimatter is stored in antimatter storage */
2563  if (obj->isVirtual || Q_streq(obj->id, ANTIMATTER_ITEM_ID))
2564  return false;
2565 
2566  return true;
2567 }
2568 
2576 int B_AddToStorage (base_t* base, const objDef_t* obj, int amount)
2577 {
2578  capacities_t* cap;
2579 
2580  assert(base);
2581  assert(obj);
2582 
2583  if (!B_ItemIsStoredInBaseStorage(obj))
2584  return 0;
2585 
2586  cap = CAP_Get(base, CAP_ITEMS);
2587  if (amount > 0) {
2588  if (obj->size > 0)
2589  cap->cur += (amount * obj->size);
2590  base->storage.numItems[obj->idx] += amount;
2591  } else if (amount < 0) {
2592  /* correct amount */
2593  const int itemInBase = B_ItemInBase(obj, base);
2594  amount = std::max(amount, -itemInBase);
2595  if (obj->size > 0)
2596  cap->cur += (amount * obj->size);
2597  base->storage.numItems[obj->idx] += amount;
2598 
2599  if (base->storage.numItems[obj->idx] == 0) {
2600  technology_t* tech = RS_GetTechForItem(obj);
2601  if (tech->statusResearch == RS_RUNNING && tech->base == base)
2602  RS_StopResearch(tech);
2603  }
2604  }
2605 
2606  return amount;
2607 }
2608 
2613 int B_AntimatterInBase (const base_t* base)
2614 {
2615 #ifdef DEBUG
2616  const objDef_t* od;
2617 
2619  if (od == nullptr)
2620  cgi->Com_Error(ERR_DROP, "Could not find " ANTIMATTER_ITEM_ID " object definition");
2621 
2622  assert(base);
2623  assert(B_ItemInBase(od, base) == CAP_GetCurrent(base, CAP_ANTIMATTER));
2624 #endif
2625 
2626  return CAP_GetCurrent(base, CAP_ANTIMATTER);
2627 }
2628 
2635 int B_AddAntimatter (base_t* base, int amount)
2636 {
2637  const objDef_t* od;
2638  capacities_t* cap;
2639 
2640  assert(base);
2641 
2643  if (od == nullptr)
2644  cgi->Com_Error(ERR_DROP, "Could not find " ANTIMATTER_ITEM_ID " object definition");
2645 
2646  cap = CAP_Get(base, CAP_ANTIMATTER);
2647  if (amount > 0) {
2648  cap->cur += amount;
2649  base->storage.numItems[od->idx] += amount;
2650  } else if (amount < 0) {
2651  /* correct amount */
2652  const int inBase = B_AntimatterInBase(base);
2653  amount = std::max(amount, -inBase);
2654  cap->cur += (amount);
2655  base->storage.numItems[od->idx] += amount;
2656  }
2657 
2658  return B_AntimatterInBase(base);
2659 }
bool mandatory
Definition: cp_building.h:105
#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:979
#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:2263
struct base_s * homebase
Definition: cp_aircraft.h:150
xmlNode_t *IMPORT * XML_GetNode(xmlNode_t *parent, const char *name)
const objDef_t * INVSH_GetItemByIDSilent(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn&#39;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:545
float level
Definition: cp_building.h:89
#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:2133
const float RADAR_BASERANGE
Definition: cp_radar.cpp:39
building_t buildingTemplates[MAX_BUILDINGS]
Definition: cp_campaign.h:339
abilityskills_t
Definition: chr_shared.h:36
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
csi_t * csi
Definition: cgame.h:100
uiMessageListNodeMessage_t * MSO_CheckAddNewMessage(const notify_t messagecategory, const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup)
Adds a new message to message stack. It uses message settings to verify whether sound should be playe...
char firstBaseTemplate[MAX_VAR]
Definition: cp_campaign.h:193
char soldierEquipment[MAX_VAR]
Definition: cp_campaign.h:171
template for creating a base
Definition: cp_base.h:130
Store capacities in base.
Definition: cp_capacity.h:41
void B_UpdateBuildingConstructions(void)
Updates base data.
Definition: cp_base.cpp:2009
bool RS_IsResearched_ptr(const technology_t *tech)
Checks whether an item is already researched.
const objDef_t * INVSH_GetItemByID(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn&#39;t found.
Definition: inv_shared.cpp:282
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
bool B_GetBuildingStatus(const base_t *const base, const buildingType_t buildingType)
Get the status associated to a building.
Definition: cp_base.cpp:478
static void B_AddBlockedTiles(base_t *base, int count)
Fuction to put blocked tiles on basemap.
Definition: cp_base.cpp:202
int experience[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:120
Item * getHolsterContainer() const
Definition: inv_shared.cpp:970
aircraft_t * AIR_NewAircraft(base_t *base, const aircraft_t *aircraftTemplate)
Places a new aircraft in the given base.
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
cvar_t *IMPORT * Cvar_Set(const char *varName, const char *value,...) __attribute__((format(__printf__
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&#39;t need to have varargs versions of all text functi...
Definition: shared.cpp:410
A base with all it&#39;s data.
Definition: cp_base.h:84
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:519
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
#define SAVE_BASES_BUILDINGS
Definition: save_base.h:43
bool hasBuilding[MAX_BUILDING_TYPE]
Definition: cp_base.h:98
#define _(String)
Definition: cl_shared.h:44
int E_CountAllHired(const base_t *const base, const bool peopleOnly)
Counts all hired employees of a given base.
char * id
Definition: cp_aircraft.h:120
void * data
Definition: list.h:31
void AIM_AutoEquipAircraft(aircraft_t *aircraft)
Auto Add weapon and ammo to an aircraft.
baseWeapon_t lasers[MAX_BASE_SLOT]
Definition: cp_base.h:119
struct building_s * tpl
Definition: cp_building.h:75
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:2576
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
struct base_s * base
Definition: cp_research.h:169
bool B_IsBuildingDestroyable(const building_t *building)
Returns if a base building is destroyable.
Definition: cp_base.cpp:224
base_t bases[MAX_BASES]
Definition: cp_campaign.h:282
void empty(void)
Empties the cargo.
Definition: itemcargo.cpp:99
#define CAP_Get(base, capacity)
Capacity macros.
Definition: cp_capacity.h:50
void B_ResetAllStatusAndCapacities(base_t *base, bool firstEnable)
Recalculate status and capacities of one base.
Definition: cp_base.cpp:706
void RS_MarkResearchable(const base_t *base, bool init)
Marks all the techs that can be researched. Automatically researches &#39;free&#39; techs such as ammo for a ...
void B_UpdateBaseCapacities(baseCapacities_t cap, base_t *base)
Updates base capacities.
Definition: cp_base.cpp:2160
int numBaseTemplates
Definition: cp_campaign.h:287
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
Describes a character with all its attributes.
Definition: chr_shared.h:388
#define SAVE_BASES_X
Definition: save_base.h:38
Class describing a point of time.
Definition: DateTime.h:30
base_t * B_GetCurrentSelectedBase(void)
returns the currently selected base
Definition: cp_base.cpp:1578
#define BASE_SIZE
Definition: cp_base.h:38
struct base_s * base
Definition: cp_building.h:76
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:1987
voidpf void uLong size
Definition: ioapi.h:42
bool B_LoadXML(xmlNode_t *parent)
Loads base data.
Definition: cp_base.cpp:2406
linkedList_t * initialCraft
Definition: cp_campaign.h:210
void INS_SetCurrentSelectedInstallation(const installation_t *installation)
Sets the currently selected installation.
char name[MAX_VAR]
Definition: cp_base.h:86
buildingStatus_t buildingStatus
Definition: cp_building.h:94
#define MAX_BASE_SLOT
Definition: cp_base.h:35
market_t eMarket
Definition: cp_campaign.h:232
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.
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...
void AL_AddAliens(aircraft_t *aircraft)
Puts alien cargo into Alien Containment.
struct radar_s radar
Definition: cp_aircraft.h:159
#define ABILITY_NUM_TYPES
Definition: chr_shared.h:54
bool isVirtual
Definition: inv_shared.h:284
bool GEO_IsRadarOverlayActivated(void)
Definition: cp_geoscape.cpp:85
void E_HireForBuilding(base_t *base, building_t *building, int num)
Hires some employees of appropriate type for a building.
const char *IMPORT * Cmd_Argv(int n)
#define SAVE_BASES_ITEM
Definition: save_base.h:58
void CP_MissionNotifyBaseDestroyed(const base_t *base)
Notify that a base has been removed.
int getTimeAsSeconds() const
Return the time part of the DateTime as seconds.
Definition: DateTime.cpp:54
#define SAVE_BASES_BASESTATUS
Definition: save_base.h:33
int basesBuilt
Definition: cp_statistics.h:32
buildingType_t
All different building types.
Definition: cp_building.h:51
#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:861
Item cargo class header.
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:1011
fireDef_t fd[MAX_WEAPONS_PER_OBJDEF][MAX_FIREDEFS_PER_WEAPON]
Definition: inv_shared.h:314
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.
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
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:63
int numBases
Definition: cp_campaign.h:283
bool autosell[MAX_OBJDEFS]
Definition: cp_market.h:33
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
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:62
const char * id
Definition: inv_shared.h:268
const char * mapPart
Definition: cp_building.h:80
#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
char * onDisable
Definition: cp_building.h:100
base_t * B_GetFoundedBaseByIDX(int baseIdx)
Array bound check for the base index.
Definition: cp_base.cpp:326
void GEO_ResetAction(void)
No more special action on the geoscape.
Header file for menu related console command callbacks.
char * onDestroy
Definition: cp_building.h:98
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.
char *IMPORT * PoolStrDup(const char *in, memPool_t *pool, const int tagNum)
int size
Definition: inv_shared.h:334
int idx
Definition: cp_base.h:85
#define xmlNode_t
Definition: xml.h:24
int B_GetCount(void)
Returns the count of founded bases.
Definition: cp_base.cpp:277
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
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:384
#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.
#define SAVE_BASES_TRACKINGRANGE
Definition: save_base.h:64
void AII_ReloadAircraftWeapons(aircraft_t *aircraft)
Reload the weapons of an aircraft.
#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
struct radar_s radar
Definition: cp_base.h:106
int numLasers
Definition: cp_base.h:120
#define ngettext(x, y, cnt)
Definition: g_local.h:40
int buildTime
Definition: cp_building.h:92
#define ERR_DROP
Definition: common.h:211
void B_DumpAircraftToHomeBase(aircraft_t *aircraft)
Will unload all cargo to the homebase.
Definition: cp_base.cpp:2084
static void B_UpdateAntimatterCap(base_t *base)
Update Antimatter Capacity.
Definition: cp_base.cpp:694
#define DEBUG_CLIENT
Definition: defines.h:59
int numBuildingTemplates
Definition: cp_campaign.h:340
xmlNode_t *IMPORT * XML_GetPos3(xmlNode_t *parent, const char *name, vec3_t pos)
aircraft_t * aircraftCurrent
Definition: cp_base.h:100
void B_Delete(base_t *base)
Resets a base structure.
Definition: cp_base.cpp:898
int weaponSkill
Definition: inv_shared.h:162
#define OBJZERO(obj)
Definition: shared.h:178
#define SAVE_BASES_NUM
Definition: save_base.h:60
xmlNode_t *IMPORT * XML_GetPos2(xmlNode_t *parent, const char *name, vec2_t pos)
#define MAX_VAR
Definition: shared.h:36
aircraft_t * AIR_GetFirstFromBase(const base_t *b)
Iterates through the aircraft of a base.
Definition: cp_aircraft.cpp:51
char * id
Definition: cp_base.h:131
bool B_SaveStorageXML(xmlNode_t *parent, const equipDef_t &equip)
Saves base storage.
Definition: cp_base.cpp:2244
#define Vector2Set(v, x, y)
Definition: vector.h:61
xmlNode_t *IMPORT * XML_GetDate(xmlNode_t *parent, const char *name, int *day, int *sec)
class DateTime timeStart
Definition: cp_building.h:91
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
Campaign missions headers.
int numItems[MAX_OBJDEFS]
Definition: inv_shared.h:608
int getDateAsDays() const
Return the date part of the DateTime as days.
Definition: DateTime.cpp:46
static linkedList_t * B_GetNeighbours(const building_t *building)
Returns the neighbourhood of a building.
Definition: cp_base.cpp:58
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition: cp_base.cpp:286
#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
bool autofire
Definition: cp_base.h:80
const char *IMPORT * XML_GetString(xmlNode_t *parent, const char *name)
#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
bool B_LoadStorageXML(xmlNode_t *parent, equipDef_t *equip)
Loads base storage.
Definition: cp_base.cpp:2387
void B_AircraftReturnedToHomeBase(aircraft_t *aircraft)
Do anything when dropship returns to base.
Definition: cp_base.cpp:2102
baseTemplate_t baseTemplates[MAX_BASETEMPLATES]
Definition: cp_campaign.h:286
#define SAVE_BASES_BUILDINGLEVEL
Definition: save_base.h:49
char equipment[MAX_VAR]
Definition: cp_campaign.h:172
aircraft_t * target
Definition: cp_base.h:79
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
void CP_UpdateCredits(int credits)
Sets credits and update mn_credits cvar.
ccs_t ccs
Definition: cp_campaign.cpp:63
int fixCosts
Definition: cp_building.h:83
#define SAVE_BASES_ALIENINTEREST
Definition: save_base.h:34
const equipDef_t *IMPORT * INV_GetEquipmentDefinitionByID(const char *name)
int numBuildings[MAX_BASES]
Definition: cp_campaign.h:344
baseBuildingTile_t buildings[MAX_BASEBUILDINGS]
Definition: cp_base.h:132
int numODs
Definition: q_shared.h:518
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:2228
#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:313
Campaign geoscape time header.
#define SAVE_BASES_IDX
Definition: save_base.h:30
linkedList_t * acTeam
Definition: cp_aircraft.h:140
xmlNode_t *IMPORT * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
vec2_t pos
Definition: cp_building.h:104
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:414
char * name
Definition: cp_building.h:79
void B_Destroy(base_t *base)
Destroy a base.
Definition: cp_base.cpp:914
void B_SetCurrentSelectedBase(const base_t *base)
Sets the selected base.
Definition: cp_base.cpp:1553
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:2030
char * onEnable
Definition: cp_building.h:99
#define SAVE_BUILDINGSTATUS_NAMESPACE
Definition: save_base.h:69
int stats[AIR_STATS_MAX]
Definition: cp_aircraft.h:160
char cp_messageBuffer[MAX_MESSAGE_TEXT]
Definition: cp_messages.cpp:33
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:390
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:202
building_t buildings[MAX_BASES][MAX_BUILDINGS]
Definition: cp_campaign.h:342
float liquidationRate
Definition: cp_campaign.h:200
#define CAP_GetMax(base, capacity)
Definition: cp_capacity.h:51
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:37
int initialSkills[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:123
baseWeapon_t batteries[MAX_BASE_SLOT]
Definition: cp_base.h:116
An aircraft with all it&#39;s data.
Definition: cp_aircraft.h:115
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
#define B_GetBuildingByIDX(baseIdx, buildingIdx)
Definition: cp_base.cpp:46
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
equipDef_t storage
Definition: cp_base.h:112
aircraft_t * UFO_GetByIDX(const int idx)
returns the UFO on the geoscape with a certain index
Definition: cp_ufo.cpp:85
vec2_t size
Definition: cp_building.h:82
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:635
#define SAVE_BASES_RADARRANGE
Definition: save_base.h:63
#define SAVE_BASES_POS
Definition: save_base.h:32
static bool B_CheckUpdateBuilding(building_t *building)
Check base status for particular buildings as well as capacities.
Definition: cp_base.cpp:600
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:2613
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:2347
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
campaign_t * curCampaign
Definition: cp_campaign.h:378
stats_t campaignStats
Definition: cp_campaign.h:379
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
const char * image
Definition: cp_building.h:80
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)
weaponFireDefIndex_t weapFdsIdx
Definition: inv_shared.h:126
void CAP_SetCurrent(base_t *base, baseCapacities_t capacity, int value)
Sets the current (used) capacity on a base.
Definition: cp_capacity.cpp:97
item cargo entry
Definition: itemcargo.h:32
static void B_BuildFromTemplate(base_t *base, const char *templateName, bool hire)
builds a base from template
Definition: cp_base.cpp:1040
Header for slot management related stuff.
This is the technology parsed from research.ufo.
Definition: cp_research.h:139
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:90
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:2119
void RS_StopResearch(technology_t *tech)
Stops a research (Removes scientists from it)
Definition: cp_research.cpp:94
int maxTeamSize
Definition: cp_aircraft.h:139
#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:2377
class DateTime date
Definition: cp_campaign.h:246
chrScoreGlobal_t score
Definition: chr_shared.h:406
const char * id
Definition: cp_building.h:78
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
Definition: inv_shared.cpp:576
#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:2635
const struct building_s * dependsBuilding
Definition: cp_building.h:112
bool isReloadable() const
Definition: inv_shared.h:479
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:2364
#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:367
#define MapIsWater(color)
Definition: cp_geoscape.h:32
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it&#39;s safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
#define Vector2Copy(src, dest)
Definition: vector.h:52
Item * getRightHandContainer() const
Definition: inv_shared.cpp:955
vec_t vec2_t[2]
Definition: ufotypes.h:38
buildingType_t buildingType
Definition: cp_building.h:110
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:1970
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
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
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:563
static bool B_AddBlockedTile(base_t *base, int row, int column)
Check and add blocked tile to the base.
Definition: cp_base.cpp:138
char name[MAX_VAR]
Definition: chr_shared.h:390
#define B_GetBuildingIDX(base, building)
Definition: cp_base.cpp:47
void BDEF_InitialiseBaseSlots(base_t *base)
Initialise all values of base slot defence.
#define lengthof(x)
Definition: shared.h:105
xmlNode_t *IMPORT * XML_AddNode(xmlNode_t *parent, const char *name)
bool founded
Definition: cp_base.h:90
XML tag constants for savegame.
A building with all it&#39;s data.
Definition: cp_building.h:73
#define Q_streq(a, b)
Definition: shared.h:136
bool CP_CheckCredits(int costs)
Checks whether you have enough credits for something.
const char * AIR_CheckMoveIntoNewHomebase(const aircraft_t *aircraft, const base_t *base)
Checks if destination base can store an aircraft and its team.
building_t * building
Definition: cp_base.h:69
mapAction_t mapAction
Definition: cp_campaign.h:261
#define SAVE_BASES_BUILDINGINDEX
Definition: save_base.h:40
bool selected
Definition: cp_base.h:126
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:771
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.
float alienInterest
Definition: cp_base.h:104
#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:607
class AlienContainment * alienContainment
Definition: cp_base.h:108
technology_t * RS_GetTechForItem(const objDef_t *item)
Returns technology entry for an item.
void GEO_SetOverlay(const char *overlayID, int status)
Turn overlay on/off.
const objDef_t * def(void) const
Definition: inv_shared.h:469
vec3_t pos
Definition: cp_base.h:91
int credits
Definition: cp_campaign.h:243
bool save(xmlNode_t *root) const
Save alien cargo to xml savegame.
Definition: aliencargo.cpp:188
void B_SetBuildingStatus(base_t *const base, const buildingType_t buildingType, bool newStatus)
Set status associated to a building.
Definition: cp_base.cpp:499
const char *IMPORT * Com_GetConstVariable(const char *space, int value)
#define SAVE_BASES_ODS_ID
Definition: save_base.h:59
#define ANTIMATTER_ITEM_ID
Definition: cp_research.h:37
const char *IMPORT * Com_EParse(const char **text, const char *errhead, const char *errinfo)
researchStatus_t statusResearch
Definition: cp_research.h:165
int numBatteries
Definition: cp_base.h:117
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:416
#define B_IsTileBlocked(base, x, y)
Definition: cp_base.h:164
#define MAX_BASETEMPLATES
Definition: cp_base.h:33
linkedList_t * list(void) const
Returns a copy of the cargo list.
Definition: itemcargo.cpp:156
void B_SelectBase(const base_t *base)
Select and opens a base.
Definition: cp_base.cpp:1592
baseStatus_t baseStatus
Definition: cp_base.h:102
#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
Inventory bEquipment
Definition: cp_base.h:114
Definition: scripts.h:55
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
baseBuildingTile_t map[BASE_SIZE][BASE_SIZE]
Definition: cp_base.h:87
void AIRFIGHT_RemoveProjectileAimingAircraft(const aircraft_t *aircraft)
Set all projectile aiming a given aircraft to an idle destination.
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
class ItemCargo * itemCargo
Definition: cp_aircraft.h:178
int numBuildings
Definition: cp_base.h:133
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:447
#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:339
Inventory inv
Definition: chr_shared.h:411
bool B_ItemIsStoredInBaseStorage(const objDef_t *obj)
Check if an item is stored in storage.
Definition: cp_base.cpp:2560
void AIR_DeleteAircraft(aircraft_t *aircraft)
Removes an aircraft from its base and the game.
buildingStatus_t
All possible building status.
Definition: cp_building.h:31