UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_base.cpp
Go to the documentation of this file.
1
7
8/*
9Copyright (C) 2002-2025 UFO: Alien Invasion.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, 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"
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
50static void B_InitialEquipment(aircraft_t* aircraft, const equipDef_t* ed);
51
58static 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
93static 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
138static 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)) {
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
202static 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
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
277int B_GetCount (void)
278{
279 return ccs.numBases;
280}
281
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
313base_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
339building_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
367building_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
390bool 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;
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;
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
478bool 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
499void 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
545static 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
563bool 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
600static 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
635static 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
694static void B_UpdateAntimatterCap (base_t* base)
695{
697 if (od != nullptr)
699}
700
706void 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
747
750
753
756
757 /* Check that current capacity is possible -- if we changed values in *.ufo */
758 for (int i = 0; i < MAX_CAP; 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 */
790 CP_UpdateCredits(ccs.credits + building->fixCosts * ccs.curCampaign->liquidationRate);
791
792 const buildingType_t buildingType = building->buildingType;
793 const building_t* buildingTemplate = building->tpl;
794 const bool runDisableCommand = building->buildingStatus == B_STATUS_WORKING;
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
898void 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
914void B_Destroy (base_t* base)
915{
916 assert(base);
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
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
949static 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
1011static 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
1040static 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
1107void 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
1173void B_SetName (base_t* base, const char* name)
1174{
1175 Q_strncpyz(base->name, name, sizeof(base->name));
1176}
1177
1185base_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
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);
1229
1231
1233
1234 ccs.campaignStats.basesBuilt++;
1235
1237
1238 return base;
1239}
1240
1246const 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
1263bool 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
1279building_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
1414const 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
1435void 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
1452 if (ccs.numBaseTemplates >= MAX_BASETEMPLATES)
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
1463 ccs.numBaseTemplates++;
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
1592void 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) {
1600 return;
1601 }
1602
1603 if (B_GetCount() < MAX_BASES) {
1604 /* show radar overlay (if not already displayed) */
1606 GEO_SetOverlay("radar", 1);
1607 ccs.mapAction = MA_NEWBASE;
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
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
1641static 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:
1649 CL_SwapSkill(cp1, cp2, ABILITY_SPEED);
1650 break;
1651#if 0
1652 case SKILL_HEAVY:
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:
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
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
1719static 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
1759static 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
1789void B_BaseResetStatus (base_t* const base)
1790{
1791 assert(base);
1792 base->baseStatus = BASE_NOT_USED;
1793}
1794
1795#ifdef DEBUG
1801static 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
1832static 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++) {
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
1874static 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
1908static 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
1937static 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
1951static 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
1970void 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
2030static 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);
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
2119bool 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
2133int 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
2228void 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
2244bool 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
2263bool 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);
2283 cgi->XML_AddString(act_base, SAVE_BASES_BASESTATUS, cgi->Com_GetConstVariable(SAVE_BASESTATUS_NAMESPACE, b->baseStatus));
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);
2311 cgi->XML_AddString(snode, SAVE_BASES_BUILDINGSTATUS, cgi->Com_GetConstVariable(SAVE_BUILDINGSTATUS_NAMESPACE, building->buildingStatus));
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
2347int 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
2364static bool B_PostLoadInitCapacity (void)
2365{
2366 base_t* base = nullptr;
2367 while ((base = B_GetNext(base)) != nullptr)
2369
2370 return true;
2371}
2372
2378{
2379 return B_PostLoadInitCapacity();
2380}
2381
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
2406bool 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 */
2448 xmlNode_t* node = cgi->XML_GetNode(base, SAVE_BASES_BUILDINGSPACE);
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
2507 str = cgi->XML_GetString(snode, SAVE_BASES_BUILDINGSTATUS);
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
2541 node = cgi->XML_GetNode(base, SAVE_BASES_ALIENCONTAINMENT);
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
2576int B_AddToStorage (base_t* base, const objDef_t* obj, int amount)
2577{
2578 capacities_t* cap;
2579
2580 assert(base);
2581 assert(obj);
2582
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
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
2635int 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}
DateTime class definition.
Alien containment class header.
abilityskills_t
Definition chr_shared.h:36
@ SKILL_HEAVY
Definition chr_shared.h:43
@ ABILITY_POWER
Definition chr_shared.h:37
@ ABILITY_SPEED
Definition chr_shared.h:38
@ SKILL_NUM_TYPES
Definition chr_shared.h:51
@ SKILL_SNIPER
Definition chr_shared.h:45
@ ABILITY_MIND
Definition chr_shared.h:40
@ SKILL_CLOSE
Definition chr_shared.h:42
@ SKILL_EXPLOSIVE
Definition chr_shared.h:46
@ ABILITY_ACCURACY
Definition chr_shared.h:39
@ SKILL_ASSAULT
Definition chr_shared.h:44
#define ABILITY_NUM_TYPES
Definition chr_shared.h:54
Header file for inventory handling and Equipment menu.
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
bool load(xmlNode_t *root)
Load alien cargo from xml savegame.
bool save(xmlNode_t *root) const
Save alien cargo to xml savegame.
Alien containment class.
Class describing a point of time.
Definition DateTime.h:31
int getTimeAsSeconds() const
Return the time part of the DateTime as seconds.
Definition DateTime.cpp:54
int getDateAsDays() const
Return the date part of the DateTime as days.
Definition DateTime.cpp:46
Item * getRightHandContainer() const
Item * getHolsterContainer() const
void init()
void empty(void)
Empties the cargo.
Definition itemcargo.cpp:99
linkedList_t * list(void) const
Returns a copy of the cargo list.
item instance data, with linked list capability
Definition inv_shared.h:402
const objDef_t * ammoDef(void) const
Definition inv_shared.h:460
const objDef_t * def(void) const
Definition inv_shared.h:469
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
bool isReloadable() const
Definition inv_shared.h:479
#define REMOVE_ELEM(array, index, n)
Definition common.h:385
#define ERR_DROP
Definition common.h:211
void AIR_AssignInitial(aircraft_t *aircraft)
Assigns initial team of soldiers to aircraft.
void AIR_DeleteAircraft(aircraft_t *aircraft)
Removes an aircraft from its base and the game.
aircraft_t * AIR_NewAircraft(base_t *base, const aircraft_t *aircraftTemplate)
Places a new aircraft in the given base.
void AIR_MoveAircraftIntoNewHomebase(aircraft_t *aircraft, base_t *base)
Moves a given aircraft to a new base (also the employees and inventory).
bool AIR_IsAircraftOnGeoscape(const aircraft_t *aircraft)
Checks whether given aircraft is on geoscape.
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.
const char * AIR_CheckMoveIntoNewHomebase(const aircraft_t *aircraft, const base_t *base)
Checks if destination base can store an aircraft and its team.
const aircraft_t * AIR_GetAircraft(const char *name)
Searches the global array of aircraft types for a given aircraft.
void AIR_DestroyAircraft(aircraft_t *aircraft, bool killPilot)
Removes an aircraft from its base and the game.
baseCapacities_t AIR_GetHangarCapacityType(const aircraft_t *aircraft)
Returns capacity type needed for an aircraft.
aircraft_t * AIR_GetFirstFromBase(const base_t *b)
Iterates through the aircraft of a base.
Header file for aircraft stuff.
#define AIR_ForeachFromBase(var, base)
iterates trough all aircraft from a specific homebase
void AIRFIGHT_RemoveProjectileAimingAircraft(const aircraft_t *aircraft)
Set all projectile aiming a given aircraft to an idle destination.
void AL_AddAliens(aircraft_t *aircraft)
Puts alien cargo into Alien Containment.
void B_ParseBaseTemplate(const char *name, const char **text)
Reads a base layout template.
Definition cp_base.cpp:1435
bool B_LoadXML(xmlNode_t *parent)
Loads base data.
Definition cp_base.cpp:2406
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
void B_DumpAircraftToHomeBase(aircraft_t *aircraft)
Will unload all cargo to the homebase.
Definition cp_base.cpp:2084
void B_BaseResetStatus(base_t *const base)
Sets the baseStatus to BASE_NOT_USED.
Definition cp_base.cpp:1789
bool B_BuildingDestroy(building_t *building)
Removes a building from the given base.
Definition cp_base.cpp:771
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
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
bool B_PostLoadInit(void)
Set the capacity stuff for all the bases after loading a savegame.
Definition cp_base.cpp:2377
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
static void CL_DoSwapSkills(character_t *cp1, character_t *cp2, const abilityskills_t skill)
Definition cp_base.cpp:1641
baseCapacities_t B_GetCapacityFromBuildingType(buildingType_t type)
Get the capacity associated to a building type.
Definition cp_base.cpp:416
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
base_t * B_GetFoundedBaseByIDX(int baseIdx)
Array bound check for the base index.
Definition cp_base.cpp:326
bool B_SaveXML(xmlNode_t *parent)
Save callback for saving in xml format.
Definition cp_base.cpp:2263
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
static bool B_CheckUpdateBuilding(building_t *building)
Check base status for particular buildings as well as capacities.
Definition cp_base.cpp:600
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
bool B_LoadStorageXML(xmlNode_t *parent, equipDef_t *equip)
Loads base storage.
Definition cp_base.cpp:2387
static void B_BuildFromTemplate(base_t *base, const char *templateName, bool hire)
builds a base from template
Definition cp_base.cpp:1040
void B_SelectBase(const base_t *base)
Select and opens a base.
Definition cp_base.cpp:1592
static bool B_PostLoadInitCapacity(void)
Set the capacity stuff for all the bases after loading a savegame.
Definition cp_base.cpp:2364
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
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition cp_base.cpp:286
void B_SetCurrentSelectedBase(const base_t *base)
Sets the selected base.
Definition cp_base.cpp:1553
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
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
#define B_GetBuildingByIDX(baseIdx, buildingIdx)
Definition cp_base.cpp:46
void B_AircraftReturnedToHomeBase(aircraft_t *aircraft)
Do anything when dropship returns to base.
Definition cp_base.cpp:2102
void B_ResetAllStatusAndCapacities(base_t *base, bool firstEnable)
Recalculate status and capacities of one base.
Definition cp_base.cpp:706
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
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
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
int B_GetCount(void)
Returns the count of founded bases.
Definition cp_base.cpp:277
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
bool B_ItemIsStoredInBaseStorage(const objDef_t *obj)
Check if an item is stored in storage.
Definition cp_base.cpp:2560
int B_GetInstallationLimit(void)
Counts the actual installation count limit.
Definition cp_base.cpp:1153
int B_AddAntimatter(base_t *base, int amount)
Manages antimatter (adding, removing) through Antimatter Storage Facility.
Definition cp_base.cpp:2635
base_t * B_GetCurrentSelectedBase(void)
returns the currently selected base
Definition cp_base.cpp:1578
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
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
bool B_IsBuildingDestroyable(const building_t *building)
Returns if a base building is destroyable.
Definition cp_base.cpp:224
void B_SetName(base_t *base, const char *name)
Set the base name.
Definition cp_base.cpp:1173
void B_Destroy(base_t *base)
Destroy a base.
Definition cp_base.cpp:914
void B_UpdateBaseCapacities(baseCapacities_t cap, base_t *base)
Updates base capacities.
Definition cp_base.cpp:2160
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
void B_Delete(base_t *base)
Resets a base structure.
Definition cp_base.cpp:898
int B_AntimatterInBase(const base_t *base)
returns the amount of antimatter stored in a base
Definition cp_base.cpp:2613
buildingType_t B_GetBuildingTypeByCapacity(baseCapacities_t cap)
Get building type by base capacity.
Definition cp_base.cpp:447
int B_GetNumberOfBuildingsInBaseByTemplate(const base_t *base, const building_t *tpl)
Counts the number of buildings of a particular type in a base.
Definition cp_base.cpp:1353
int 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
int B_AddToStorage(base_t *base, const objDef_t *obj, int amount)
Add/remove items to/from the storage.
Definition cp_base.cpp:2576
void B_UpdateBuildingConstructions(void)
Updates base data.
Definition cp_base.cpp:2009
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
bool B_SaveStorageXML(xmlNode_t *parent, const equipDef_t &equip)
Saves base storage.
Definition cp_base.cpp:2244
static linkedList_t * B_GetNeighbours(const building_t *building)
Returns the neighbourhood of a building.
Definition cp_base.cpp:58
base_t * B_GetFirstUnfoundedBase(void)
Get the first unfounded base.
Definition cp_base.cpp:1537
static bool B_AddBlockedTile(base_t *base, int row, int column)
Check and add blocked tile to the base.
Definition cp_base.cpp:138
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_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
void B_InitStartup(void)
Resets console commands.
Definition cp_base.cpp:1970
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
building_t * B_GetNextBuilding(const base_t *base, building_t *lastBuilding)
Iterates through buildings in a base.
Definition cp_base.cpp:339
bool B_MapIsCellFree(const base_t *base, int col, int row)
Check a base cell.
Definition cp_base.cpp:1263
void B_SetBuildingStatus(base_t *const base, const buildingType_t buildingType, bool newStatus)
Set status associated to a building.
Definition cp_base.cpp:499
static void B_UpdateAntimatterCap(base_t *base)
Update Antimatter Capacity.
Definition cp_base.cpp:694
bool B_GetBuildingStatus(const base_t *const base, const buildingType_t buildingType)
Get the status associated to a building.
Definition cp_base.cpp:478
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
static void B_SellOrAddItems(aircraft_t *aircraft)
Sell items to the market or add them to base storage.
Definition cp_base.cpp:2030
#define B_GetBuildingIDX(base, building)
Definition cp_base.cpp:47
static void B_AddBlockedTiles(base_t *base, int count)
Fuction to put blocked tiles on basemap.
Definition cp_base.cpp:202
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
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
#define BASE_TILE_UNITS
Definition cp_base.h:49
#define B_IsTileBlocked(base, x, y)
Definition cp_base.h:164
#define BASE_SIZE
Definition cp_base.h:38
#define B_GetBuildingAt(base, x, y)
Definition cp_base.h:165
#define MIN_BLOCKEDFIELDS
Definition cp_base.h:42
@ BASE_WORKING
Definition cp_base.h:64
@ BASE_NOT_USED
Definition cp_base.h:62
@ BASE_DESTROYED
Definition cp_base.h:65
#define MAX_BASETEMPLATES
Definition cp_base.h:33
#define MAX_BASEBUILDINGS
Definition cp_base.h:39
#define BASE_INITIALINTEREST
Definition cp_base.h:51
#define MAX_BLOCKEDFIELDS
Definition cp_base.h:41
#define MAX_BASES
Definition cp_base.h:32
#define MAX_BUILDINGS
Definition cp_base.h:34
#define MAX_BASE_SLOT
Definition cp_base.h:35
Header file for menu related console command callbacks.
bool B_FireEvent(const building_t *buildingTemplate, const base_t *base, buildingEvent_t eventType)
Run eventhandler script for a building.
bool B_CheckBuildingDependencesStatus(const building_t *building)
Check that the dependences of a building is operationnal.
bool B_IsBuildingBuiltUp(const building_t *building)
Returns if a building is fully buildt up.
building_t * B_GetBuildingTemplate(const char *buildingName)
Returns the building in the global building-types list that has the unique name buildingID.
@ B_ONDISABLE
Definition cp_building.h:44
@ B_ONENABLE
Definition cp_building.h:43
@ B_ONCONSTRUCT
Definition cp_building.h:42
@ B_ONDESTROY
Definition cp_building.h:45
buildingType_t
All different building types.
Definition cp_building.h:51
@ B_QUARTERS
Definition cp_building.h:54
@ B_COMMAND
Definition cp_building.h:62
@ B_RADAR
Definition cp_building.h:67
@ B_HANGAR
Definition cp_building.h:58
@ MAX_BUILDING_TYPE
Definition cp_building.h:69
@ B_MISC
Definition cp_building.h:52
@ B_STORAGE
Definition cp_building.h:55
@ B_LAB
Definition cp_building.h:53
@ B_WORKSHOP
Definition cp_building.h:56
@ B_SMALL_HANGAR
Definition cp_building.h:60
@ B_ALIEN_CONTAINMENT
Definition cp_building.h:59
@ B_ANTIMATTER
Definition cp_building.h:63
buildingStatus_t
All possible building status.
Definition cp_building.h:31
@ B_STATUS_CONSTRUCTION_FINISHED
Definition cp_building.h:34
@ B_STATUS_NOT_SET
Definition cp_building.h:32
@ B_STATUS_UNDER_CONSTRUCTION
Definition cp_building.h:33
@ B_STATUS_WORKING
Definition cp_building.h:36
memPool_t * cp_campaignPool
bool CP_CheckCredits(int costs)
Checks whether you have enough credits for something.
void CP_UpdateCredits(int credits)
Sets credits and update mn_credits cvar.
ccs_t ccs
Header file for single player campaign control.
const cgame_import_t * cgi
@ MA_NONE
@ MA_NEWBASE
@ MAPTYPE_TERRAIN
Definition cp_campaign.h:93
void CAP_AddMax(base_t *base, baseCapacities_t capacity, int value)
Changes the maximal capacity on a base.
void CAP_UpdateStorageCap(base_t *base)
Update Storage Capacity.
void CAP_CheckOverflow(void)
Checks capacity overflows on bases.
int CAP_GetFreeCapacity(const base_t *base, baseCapacities_t capacityType)
Returns the free capacity of a type.
void CAP_SetMax(base_t *base, baseCapacities_t capacity, int value)
Sets the maximal capacity on a base.
void CAP_SetCurrent(base_t *base, baseCapacities_t capacity, int value)
Sets the current (used) capacity on a base.
#define CAP_GetCurrent(base, capacity)
Definition cp_capacity.h:52
baseCapacities_t
All possible capacities in base.
Definition cp_capacity.h:27
@ CAP_ANTIMATTER
Definition cp_capacity.h:35
@ CAP_WORKSPACE
Definition cp_capacity.h:34
@ CAP_ITEMS
Definition cp_capacity.h:32
@ MAX_CAP
Definition cp_capacity.h:37
@ CAP_LABSPACE
Definition cp_capacity.h:33
@ CAP_AIRCRAFT_BIG
Definition cp_capacity.h:30
@ CAP_EMPLOYEES
Definition cp_capacity.h:31
@ CAP_AIRCRAFT_SMALL
Definition cp_capacity.h:29
@ CAP_ALIENS
Definition cp_capacity.h:28
#define CAP_GetMax(base, capacity)
Definition cp_capacity.h:51
#define CAP_Get(base, capacity)
Capacity macros.
Definition cp_capacity.h:50
bool E_HireEmployeeByType(base_t *base, employeeType_t type)
Hires the first free employee of that type.
int E_CountAllHired(const base_t *const base, const bool peopleOnly)
Counts all hired employees of a given base.
void E_DeleteAllEmployees(base_t *base)
Removes all employees completely from the game (buildings + global list) from a given base.
void E_HireForBuilding(base_t *base, building_t *building, int num)
Hires some employees of appropriate type for a building.
@ EMPL_PILOT
Definition cp_employee.h:34
void GEO_SetOverlay(const char *overlayID, int status)
Turn overlay on/off.
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...
bool GEO_IsRadarOverlayActivated(void)
void GEO_ResetAction(void)
No more special action on the geoscape.
Header for Geoscape management.
#define MapIsWater(color)
Definition cp_geoscape.h:32
void INS_SetCurrentSelectedInstallation(const installation_t *installation)
Sets the currently selected installation.
#define MAX_INSTALLATIONS_PER_BASE
void AIM_AutoEquipAircraft(aircraft_t *aircraft)
Auto Add weapon and ammo to an aircraft.
void AII_LoadOneSlotXML(xmlNode_t *node, aircraftSlot_t *slot, bool weapon)
Loads one slot (base, installation or aircraft).
void AII_ReloadAircraftWeapons(aircraft_t *aircraft)
Reload the weapons of an aircraft.
void BDEF_InitialiseBaseSlots(base_t *base)
Initialise all values of base slot defence.
void AII_SaveOneSlotXML(xmlNode_t *p, const aircraftSlot_t *slot, bool weapon)
Save callback for savegames in XML Format.
Header for slot management related stuff.
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
bool BS_SellItem(const objDef_t *od, base_t *base, int count)
Sells items from the market.
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...
@ NT_BUILDING_FINISHED
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.
char cp_messageBuffer[MAX_MESSAGE_TEXT]
void CP_MissionNotifyBaseDestroyed(const base_t *base)
Notify that a base has been removed.
void CP_SpawnRescueMission(aircraft_t *aircraft, aircraft_t *ufo)
Spawn a new rescue mission for a crashed (phalanx) aircraft.
Campaign missions headers.
void PR_UpdateProductionCap(base_t *base, int workerChange)
Update the current capacity of Workshop.
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
const float RADAR_BASETRACKINGRANGE
Definition cp_radar.cpp:40
const float RADAR_BASERANGE
Definition cp_radar.cpp:39
void RADAR_InitialiseUFOs(radar_t *radar)
Reset UFO sensored on radar.
Definition cp_radar.cpp:265
void RS_MarkCollected(technology_t *tech)
Marks a give technology as collected.
int RS_CountScientistsInBase(const base_t *base)
Returns the number of employees searching in labs in given base.
technology_t * RS_GetTechForItem(const objDef_t *item)
Returns technology entry for an item.
bool RS_IsResearched_ptr(const technology_t *tech)
Checks whether an item is already researched.
void RS_StopResearch(technology_t *tech)
Stops a research (Removes scientists from it).
void RS_MarkResearchable(const base_t *base, bool init)
Marks all the techs that can be researched. Automatically researches 'free' techs such as ammo for a ...
@ RS_RUNNING
Definition cp_research.h:42
#define ANTIMATTER_ITEM_ID
Definition cp_research.h:37
#define FOREACH_XMLNODE(var, node, name)
Definition cp_save.h:54
Campaign geoscape time header.
void UFO_NotifyPhalanxAircraftRemoved(const aircraft_t *const aircraft)
Notify to UFOs that a Phalanx aircraft has been destroyed.
Definition cp_ufo.cpp:972
aircraft_t * UFO_GetByIDX(const int idx)
returns the UFO on the geoscape with a certain index
Definition cp_ufo.cpp:85
#define DEBUG_CLIENT
Definition defines.h:59
level_locals_t level
Definition g_main.cpp:38
#define ngettext(x, y, cnt)
Definition g_local.h:40
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
const objDef_t * INVSH_GetItemByID(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn't found.
const objDef_t * INVSH_GetItemByIDSilent(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn't found.
@ AIR_STATS_FUELSIZE
Definition inv_shared.h:232
voidpf void uLong size
Definition ioapi.h:42
Item cargo class header.
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition list.h:41
float frand(void)
Return random values between 0 and 1.
Definition mathlib.cpp:506
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
Shared parsing functions.
QGL_EXTERN GLuint count
Definition r_gl.h:99
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
XML tag constants for savegame.
#define SAVE_BASES_AUTOFIRE
Definition save_base.h:54
#define SAVE_BASES_BUILDINGTYPE
Definition save_base.h:44
#define SAVE_BASES_BASE
Definition save_base.h:29
#define SAVE_BASES_TARGET
Definition save_base.h:55
#define SAVE_BASES_ITEM
Definition save_base.h:58
#define SAVE_BASES_Y
Definition save_base.h:39
#define SAVE_BASES_RADARRANGE
Definition save_base.h:63
#define SAVE_BASES_STORAGE
Definition save_base.h:57
#define SAVE_BASES_X
Definition save_base.h:38
#define SAVE_BASES_ALIENCONTAINMENT
Definition save_base.h:66
#define SAVE_BASES_ODS_ID
Definition save_base.h:59
#define SAVE_BASES_BATTERIES
Definition save_base.h:51
#define SAVE_BASES_NUM
Definition save_base.h:60
#define SAVE_BASES_BASES
Definition save_base.h:27
#define SAVE_BASES_BUILDING_PLACE
Definition save_base.h:45
#define SAVE_BASES_TRACKINGRANGE
Definition save_base.h:64
#define SAVE_BASES_BUILDINGLEVEL
Definition save_base.h:49
#define SAVE_BASES_BUILDINGS
Definition save_base.h:43
#define SAVE_BASES_POS
Definition save_base.h:32
#define SAVE_BASES_BUILDINGBUILDTIME
Definition save_base.h:48
#define SAVE_BASES_NUMLOOSE
Definition save_base.h:61
static const constListEntry_t saveBaseConstants[]
Definition save_base.h:70
#define SAVE_BASESTATUS_NAMESPACE
Definition save_base.h:68
#define SAVE_BASES_BUILDINGSTATUS
Definition save_base.h:46
#define SAVE_BASES_BUILDINGSPACE
Definition save_base.h:36
#define SAVE_BASES_BUILDING
Definition save_base.h:37
#define SAVE_BUILDINGSTATUS_NAMESPACE
Definition save_base.h:69
#define SAVE_BASES_IDX
Definition save_base.h:30
#define SAVE_BASES_LASERS
Definition save_base.h:52
#define SAVE_BASES_BUILDINGTIMESTART
Definition save_base.h:47
#define SAVE_BASES_BASESTATUS
Definition save_base.h:33
#define SAVE_BASES_BLOCKED
Definition save_base.h:41
#define SAVE_BASES_ALIENINTEREST
Definition save_base.h:34
#define SAVE_BASES_WEAPON
Definition save_base.h:53
#define SAVE_BASES_NAME
Definition save_base.h:31
#define SAVE_BASES_BUILDINGINDEX
Definition save_base.h:40
@ V_POS
Definition scripts.h:55
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
#define lengthof(x)
Definition shared.h:105
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition shared.cpp:475
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
An aircraft with all it's data.
struct base_s * homebase
int stats[AIR_STATS_MAX]
linkedList_t * acTeam
class ItemCargo * itemCargo
struct radar_s radar
A base with all it's data.
Definition cp_base.h:84
int numLasers
Definition cp_base.h:120
aircraft_t * aircraftCurrent
Definition cp_base.h:100
struct radar_s radar
Definition cp_base.h:106
class AlienContainment * alienContainment
Definition cp_base.h:108
baseWeapon_t batteries[MAX_BASE_SLOT]
Definition cp_base.h:116
baseWeapon_t lasers[MAX_BASE_SLOT]
Definition cp_base.h:119
bool founded
Definition cp_base.h:90
float alienInterest
Definition cp_base.h:104
baseBuildingTile_t map[BASE_SIZE][BASE_SIZE]
Definition cp_base.h:87
baseStatus_t baseStatus
Definition cp_base.h:102
Inventory bEquipment
Definition cp_base.h:114
int idx
Definition cp_base.h:85
char name[MAX_VAR]
Definition cp_base.h:86
bool hasBuilding[MAX_BUILDING_TYPE]
Definition cp_base.h:98
vec3_t pos
Definition cp_base.h:91
int numBatteries
Definition cp_base.h:117
bool selected
Definition cp_base.h:126
equipDef_t storage
Definition cp_base.h:112
building_t * building
Definition cp_base.h:69
template for creating a base
Definition cp_base.h:130
baseBuildingTile_t buildings[MAX_BASEBUILDINGS]
Definition cp_base.h:132
aircraft_t * target
Definition cp_base.h:79
bool autofire
Definition cp_base.h:80
A building with all it's data.
Definition cp_building.h:73
struct building_s * tpl
Definition cp_building.h:75
vec2_t size
Definition cp_building.h:82
const char * image
Definition cp_building.h:80
char * name
Definition cp_building.h:79
const struct building_s * dependsBuilding
char * onEnable
Definition cp_building.h:99
float level
Definition cp_building.h:89
buildingType_t buildingType
struct base_s * base
Definition cp_building.h:76
char * onDisable
buildingStatus_t buildingStatus
Definition cp_building.h:94
class DateTime timeStart
Definition cp_building.h:91
const char * mapPart
Definition cp_building.h:80
const char * id
Definition cp_building.h:78
char * onDestroy
Definition cp_building.h:98
char firstBaseTemplate[MAX_VAR]
char soldierEquipment[MAX_VAR]
char equipment[MAX_VAR]
linkedList_t * initialCraft
Store capacities in base.
Definition cp_capacity.h:41
Describes a character with all its attributes.
Definition chr_shared.h:388
chrScoreGlobal_t score
Definition chr_shared.h:406
char name[MAX_VAR]
Definition chr_shared.h:390
Inventory inv
Definition chr_shared.h:411
int initialSkills[SKILL_NUM_TYPES+1]
Definition chr_shared.h:123
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
int experience[SKILL_NUM_TYPES+1]
Definition chr_shared.h:120
int numItems[MAX_OBJDEFS]
Definition inv_shared.h:608
byte numItemsLoose[MAX_OBJDEFS]
Definition inv_shared.h:609
this is a fire definition for our weapons/ammo
Definition inv_shared.h:110
int weaponSkill
Definition inv_shared.h:162
weaponFireDefIndex_t weapFdsIdx
Definition inv_shared.h:126
item cargo entry
Definition itemcargo.h:32
void * data
Definition list.h:31
linkedList_t * next
Definition list.h:32
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
bool isVirtual
Definition inv_shared.h:284
fireDef_t fd[MAX_WEAPONS_PER_OBJDEF][MAX_FIREDEFS_PER_WEAPON]
Definition inv_shared.h:314
const char * id
Definition inv_shared.h:268
This is the technology parsed from research.ufo.
researchStatus_t statusResearch
struct base_s * base
vec_t vec2_t[2]
Definition ufotypes.h:38
#define Vector2Set(v, x, y)
Definition vector.h:61
#define Vector2Copy(src, dest)
Definition vector.h:52
xmlNode_t * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
Get next Node of the XML tree by name.
Definition xml.cpp:508
#define xmlNode_t
Definition xml.h:24