00001
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "../client.h"
00028 #include "../cl_inventory.h"
00029 #include "../ui/ui_main.h"
00030 #include "../ui/ui_popup.h"
00031 #include "../../shared/parse.h"
00032 #include "cp_campaign.h"
00033 #include "cp_mapfightequip.h"
00034 #include "cp_aircraft.h"
00035 #include "cp_missions.h"
00036 #include "cp_map.h"
00037 #include "cp_popup.h"
00038 #include "cp_time.h"
00039 #include "cp_base_callbacks.h"
00040 #include "cp_ufo.h"
00041 #include "save/save_base.h"
00042
00043 vec2_t newBasePos;
00044 static cvar_t *cp_initial_equipment;
00045
00046 static void B_PackInitialEquipment(aircraft_t *aircraft, const equipDef_t *ed);
00047
00054 base_t* B_GetBaseByIDX (int baseIdx)
00055 {
00056 if (baseIdx >= MAX_BASES || baseIdx < 0)
00057 return NULL;
00058
00059 return &ccs.bases[baseIdx];
00060 }
00061
00067 base_t* B_GetFoundedBaseByIDX (int baseIdx)
00068 {
00069 base_t *base = B_GetBaseByIDX(baseIdx);
00070
00071 if (base && base->founded)
00072 return base;
00073
00074 return NULL;
00075 }
00076
00082 building_t* B_GetNextBuilding (const base_t *base, building_t *lastBuilding)
00083 {
00084 building_t *endOfBuildings = &ccs.buildings[base->idx][ccs.numBuildings[base->idx]];
00085 building_t *building;
00086
00087 if (!ccs.numBuildings[base->idx])
00088 return NULL;
00089
00090 if (!lastBuilding)
00091 return ccs.buildings[base->idx];
00092 assert(lastBuilding >= ccs.buildings[base->idx]);
00093 assert(lastBuilding < endOfBuildings);
00094
00095 building = lastBuilding;
00096
00097 building++;
00098 if (building >= endOfBuildings)
00099 return NULL;
00100 else
00101 return building;
00102 }
00103
00111 building_t* B_GetNextBuildingByType (const base_t *base, building_t *lastBuilding, buildingType_t buildingType)
00112 {
00113 building_t *building = lastBuilding;
00114
00115 while ((building = B_GetNextBuilding(base, building))) {
00116 if (building->buildingType == buildingType)
00117 break;
00118 }
00119 return building;
00120 }
00121
00134 qboolean B_CheckBuildingTypeStatus (const base_t* const base, buildingType_t type, buildingStatus_t status, int *cnt)
00135 {
00136 int cntlocal = 0;
00137 building_t *building = NULL;
00138
00139 while ((building = B_GetNextBuildingByType(base, building, type))) {
00140 if (building->buildingStatus == status) {
00141 cntlocal++;
00142
00143 if (!cnt)
00144 return qtrue;
00145 }
00146 }
00147
00148
00149 if (cnt)
00150 *cnt = cntlocal;
00151
00152 return cntlocal ? qtrue : qfalse;
00153 }
00154
00160 baseCapacities_t B_GetCapacityFromBuildingType (buildingType_t type)
00161 {
00162 switch (type) {
00163 case B_LAB:
00164 return CAP_LABSPACE;
00165 case B_QUARTERS:
00166 return CAP_EMPLOYEES;
00167 case B_STORAGE:
00168 return CAP_ITEMS;
00169 case B_WORKSHOP:
00170 return CAP_WORKSPACE;
00171 case B_HANGAR:
00172 return CAP_AIRCRAFT_BIG;
00173 case B_ALIEN_CONTAINMENT:
00174 return CAP_ALIENS;
00175 case B_SMALL_HANGAR:
00176 return CAP_AIRCRAFT_SMALL;
00177 case B_ANTIMATTER:
00178 return CAP_ANTIMATTER;
00179 default:
00180 return MAX_CAP;
00181 }
00182 }
00183
00191 buildingType_t B_GetBuildingTypeByCapacity (baseCapacities_t cap)
00192 {
00193 switch (cap) {
00194 case CAP_ALIENS:
00195 return B_ALIEN_CONTAINMENT;
00196 case CAP_AIRCRAFT_SMALL:
00197 return B_SMALL_HANGAR;
00198 case CAP_AIRCRAFT_BIG:
00199 return B_HANGAR;
00200 case CAP_EMPLOYEES:
00201 return B_QUARTERS;
00202 case CAP_ITEMS:
00203 return B_STORAGE;
00204 case CAP_LABSPACE:
00205 return B_LAB;
00206 case CAP_WORKSPACE:
00207 return B_WORKSHOP;
00208 case CAP_ANTIMATTER:
00209 return B_ANTIMATTER;
00210 default:
00211 return MAX_BUILDING_TYPE;
00212 }
00213 }
00214
00222 qboolean B_GetBuildingStatus (const base_t* const base, const buildingType_t buildingType)
00223 {
00224 assert(base);
00225
00226 if (buildingType == B_MISC)
00227 return qtrue;
00228 else if (buildingType < MAX_BUILDING_TYPE)
00229 return base->hasBuilding[buildingType];
00230 else {
00231 Com_Printf("B_GetBuildingStatus: Building-type %i does not exist.\n", buildingType);
00232 return qfalse;
00233 }
00234 }
00235
00236
00244 void B_SetBuildingStatus (base_t* const base, const buildingType_t buildingType, qboolean newStatus)
00245 {
00246 assert(base);
00247
00248 if (buildingType == B_MISC)
00249 Com_Printf("B_SetBuildingStatus: No status is associated to B_MISC type of building.\n");
00250 else if (buildingType < MAX_BUILDING_TYPE) {
00251 base->hasBuilding[buildingType] = newStatus;
00252 Com_DPrintf(DEBUG_CLIENT, "B_SetBuildingStatus: set status for %i to %i\n", buildingType, newStatus);
00253 } else
00254 Com_Printf("B_SetBuildingStatus: Type of building %i does not exist\n", buildingType);
00255 }
00256
00263 qboolean B_CheckBuildingDependencesStatus (const base_t* const base, const building_t* building)
00264 {
00265 assert(base);
00266 assert(building);
00267
00268 if (!building->dependsBuilding)
00269 return qtrue;
00270
00271
00272 assert(building->dependsBuilding == building->dependsBuilding->tpl);
00273
00274 return B_GetBuildingStatus(base, building->dependsBuilding->buildingType);
00275 }
00276
00283 void B_ResetBuildingCurrent (base_t* base)
00284 {
00285 if (base)
00286 base->buildingCurrent = NULL;
00287 ccs.baseAction = BA_NONE;
00288 }
00289
00296 static const value_t valid_building_vars[] = {
00297 {"map_name", V_CLIENT_HUNK_STRING, offsetof(building_t, mapPart), 0},
00298 {"max_count", V_INT, offsetof(building_t, maxCount), MEMBER_SIZEOF(building_t, maxCount)},
00299 {"level", V_FLOAT, offsetof(building_t, level), MEMBER_SIZEOF(building_t, level)},
00300 {"name", V_TRANSLATION_STRING, offsetof(building_t, name), 0},
00301 {"pedia", V_CLIENT_HUNK_STRING, offsetof(building_t, pedia), 0},
00302 {"status", V_INT, offsetof(building_t, buildingStatus), MEMBER_SIZEOF(building_t, buildingStatus)},
00303 {"image", V_CLIENT_HUNK_STRING, offsetof(building_t, image), 0},
00304 {"visible", V_BOOL, offsetof(building_t, visible), MEMBER_SIZEOF(building_t, visible)},
00305 {"needs", V_CLIENT_HUNK_STRING, offsetof(building_t, needs), 0},
00306 {"fixcosts", V_INT, offsetof(building_t, fixCosts), MEMBER_SIZEOF(building_t, fixCosts)},
00307 {"varcosts", V_INT, offsetof(building_t, varCosts), MEMBER_SIZEOF(building_t, varCosts)},
00308 {"build_time", V_INT, offsetof(building_t, buildTime), MEMBER_SIZEOF(building_t, buildTime)},
00309 {"starting_employees", V_INT, offsetof(building_t, maxEmployees), MEMBER_SIZEOF(building_t, maxEmployees)},
00310 {"capacity", V_INT, offsetof(building_t, capacity), MEMBER_SIZEOF(building_t, capacity)},
00312
00313 {"onconstruct", V_STRING, offsetof(building_t, onConstruct), 0},
00314 {"onattack", V_STRING, offsetof(building_t, onAttack), 0},
00315 {"ondestroy", V_STRING, offsetof(building_t, onDestroy), 0},
00316 {"mandatory", V_BOOL, offsetof(building_t, mandatory), MEMBER_SIZEOF(building_t, mandatory)},
00317 {NULL, 0, 0, 0}
00318 };
00319
00327 float B_GetMaxBuildingLevel (const base_t* base, const buildingType_t type)
00328 {
00329 float max = 0.0f;
00330
00331 if (B_GetBuildingStatus(base, type)) {
00332 building_t *building = NULL;
00333 while ((building = B_GetNextBuildingByType(base, building, type)))
00334 if (building->buildingStatus == B_STATUS_WORKING)
00335 max = max(building->level, max);
00336 }
00337
00338 return max;
00339 }
00340
00349 qboolean B_AssembleMap (const base_t *base)
00350 {
00351 int row, col;
00352 char maps[2048];
00353 char coords[2048];
00354 qboolean used[MAX_BUILDINGS];
00355
00356 if (!base) {
00357 Com_Printf("B_AssembleMap: No base to assemble\n");
00358 return qfalse;
00359 }
00360
00361 maps[0] = '\0';
00362 coords[0] = '\0';
00363
00364
00365 memset(used, 0, sizeof(used));
00366
00367 for (row = 0; row < BASE_SIZE; row++) {
00368 for (col = 0; col < BASE_SIZE; col++) {
00369 if (base->map[row][col].building) {
00370 const building_t *entry = base->map[row][col].building;
00371
00372
00373
00374 if (entry->needs) {
00375 if (B_BuildingGetUsed(used, entry->idx))
00376 continue;
00377 B_BuildingSetUsed(used, entry->idx);
00378 }
00379
00380 if (!entry->mapPart)
00381 Com_Error(ERR_DROP, "MapPart for building '%s' is missing'", entry->id);
00382
00383 Q_strcat(maps, va("b/%s ", entry->mapPart), sizeof(maps));
00384 } else {
00385 Q_strcat(maps, "b/empty ", sizeof(maps));
00386 }
00387
00388 Q_strcat(coords, va("%i %i %i ", col * BASE_TILE_UNITS, (BASE_SIZE - row - 1) * BASE_TILE_UNITS, 0), sizeof(coords));
00389 }
00390 }
00391
00392
00393 cl.mapMaxLevel = BASE_MAX_WORLDLEVEL;
00394
00395 SAV_QuickSave();
00396
00397 Cbuf_AddText(va("map %s \"%s\" \"%s\"\n", (MAP_IsNight(base->pos) ? "night" : "day"), maps, coords));
00398
00399 return qtrue;
00400 }
00401
00409 static qboolean B_CheckUpdateBuilding (building_t* building, base_t* base)
00410 {
00411 qboolean oldValue;
00412
00413 assert(base);
00414 assert(building);
00415
00416
00417 if (building->buildingType == B_MISC)
00418 return qfalse;
00419
00420 oldValue = B_GetBuildingStatus(base, building->buildingType);
00421 if (building->buildingStatus == B_STATUS_WORKING
00422 && B_CheckBuildingDependencesStatus(base, building))
00423 B_SetBuildingStatus(base, building->buildingType, qtrue);
00424 else
00425 B_SetBuildingStatus(base, building->buildingType, qfalse);
00426
00427 if (B_GetBuildingStatus(base, building->buildingType) != oldValue) {
00428 Com_DPrintf(DEBUG_CLIENT, "Status of building %s is changed to %i.\n",
00429 building->name, B_GetBuildingStatus(base, building->buildingType));
00430 return qtrue;
00431 }
00432
00433 return qfalse;
00434 }
00435
00445 static void B_UpdateOneBaseBuildingStatusOnEnable (buildingType_t type, base_t* base)
00446 {
00447 switch (type) {
00448 case B_RADAR:
00449 Cmd_ExecuteString(va("update_base_radar_coverage %i", base->idx));
00450 break;
00451 case B_COMMAND:
00452 Cmd_ExecuteString("mn_installation_update_max_count");
00453 break;
00454 default:
00455 break;
00456 }
00457 }
00458
00468 static void B_UpdateOneBaseBuildingStatusOnDisable (buildingType_t type, base_t* base)
00469 {
00470 switch (type) {
00471 case B_ALIEN_CONTAINMENT:
00472
00473 AC_KillAll(base);
00474 break;
00475 case B_RADAR:
00476 Cmd_ExecuteString(va("update_base_radar_coverage %i", base->idx));
00477 break;
00478 case B_COMMAND:
00479 Cmd_ExecuteString("mn_installation_update_max_count");
00480 break;
00481 default:
00482 break;
00483 }
00484 }
00485
00495 static qboolean B_UpdateStatusBuilding (base_t* base, buildingType_t buildingType, qboolean onBuilt)
00496 {
00497 qboolean test = qfalse;
00498 qboolean returnValue = qfalse;
00499 building_t *building = NULL;
00500
00501
00502
00503 while ((building = B_GetNextBuilding(base, building))) {
00504 building_t *dependsBuilding = building->dependsBuilding;
00505 if (dependsBuilding && buildingType == dependsBuilding->buildingType) {
00506
00507 if (onBuilt && !B_GetBuildingStatus(base, building->buildingType)) {
00508
00509 if (B_CheckUpdateBuilding(building, base)) {
00510 B_UpdateOneBaseBuildingStatusOnEnable(building->buildingType, base);
00511 test = qtrue;
00512 returnValue = qtrue;
00513 }
00514 } else if (!onBuilt && B_GetBuildingStatus(base, building->buildingType)) {
00515
00516 if (B_CheckUpdateBuilding(building, base)) {
00517 B_UpdateOneBaseBuildingStatusOnDisable(building->buildingType, base);
00518 test = qtrue;
00519 returnValue = qtrue;
00520 }
00521 }
00522 }
00523 }
00524
00525
00526 while (test) {
00527 test = qfalse;
00528 building = NULL;
00529 while ((building = B_GetNextBuilding(base, building))) {
00530 if (onBuilt && !B_GetBuildingStatus(base, building->buildingType)) {
00531
00532 if (B_CheckUpdateBuilding(building, base)) {
00533 B_UpdateOneBaseBuildingStatusOnEnable(building->buildingType, base);
00534 test = qtrue;
00535 }
00536 } else if (!onBuilt && B_GetBuildingStatus(base, building->buildingType)) {
00537
00538 if (B_CheckUpdateBuilding(building, base)) {
00539 B_UpdateOneBaseBuildingStatusOnDisable(building->buildingType, base);
00540 test = qtrue;
00541 }
00542 }
00543 }
00544 }
00545
00546 return returnValue;
00547 }
00548
00554 static void B_UpdateAntimatterCap (base_t *base)
00555 {
00556 objDef_t *od = INVSH_GetItemByID(ANTIMATTER_TECH_ID);
00557 if (od != NULL)
00558 base->capacities[CAP_ANTIMATTER].cur = base->storage.numItems[od->idx];
00559 }
00560
00567 int B_FreeCapacity (const base_t *base, baseCapacities_t cap)
00568 {
00569 assert(base);
00570 return base->capacities[cap].max - base->capacities[cap].cur;
00571 }
00572
00579 void B_ResetAllStatusAndCapacities (base_t *base, qboolean firstEnable)
00580 {
00581 int i;
00582 qboolean test = qtrue;
00583
00584 assert(base);
00585
00586 Com_DPrintf(DEBUG_CLIENT, "Reseting base %s:\n", base->name);
00587
00588
00589 for (i = 0; i < MAX_BUILDING_TYPE; i++) {
00590 if (i != B_MISC)
00591 B_SetBuildingStatus(base, i, qfalse);
00592 }
00593
00594 while (test) {
00595 building_t *building = NULL;
00596 test = qfalse;
00597 while ((building = B_GetNextBuilding(base, building))) {
00598 if (!B_GetBuildingStatus(base, building->buildingType)
00599 && B_CheckUpdateBuilding(building, base)) {
00600 if (firstEnable)
00601 B_UpdateOneBaseBuildingStatusOnEnable(building->buildingType, base);
00602 test = qtrue;
00603 }
00604 }
00605 }
00606
00607
00608 B_UpdateBaseCapacities(MAX_CAP, base);
00609
00610
00611 if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_ALIENS)))
00612 base->capacities[CAP_ALIENS].cur = AL_CountInBase(base);
00613
00614 if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_AIRCRAFT_SMALL)) ||
00615 B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_AIRCRAFT_BIG)))
00616 AIR_UpdateHangarCapForAll(base);
00617
00618 if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_EMPLOYEES)))
00619 base->capacities[CAP_EMPLOYEES].cur = E_CountAllHired(base);
00620
00621 if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_ITEMS)))
00622 B_UpdateStorageCap(base);
00623
00624 if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_LABSPACE)))
00625 base->capacities[CAP_LABSPACE].cur = RS_CountScientistsInBase(base);
00626
00627 if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_WORKSPACE)))
00628 PR_UpdateProductionCap(base);
00629
00630 if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_ANTIMATTER)))
00631 B_UpdateAntimatterCap(base);
00632
00633
00634 for (i = 0; i < MAX_CAP; i++)
00635 if (base->capacities[i].cur > base->capacities[i].max)
00636 Com_Printf("B_ResetAllStatusAndCapacities: Warning, capacity of %i is bigger than maximum capacity\n", i);
00637 }
00638
00650 void B_RemoveAircraftExceedingCapacity (base_t* base, buildingType_t buildingType)
00651 {
00652 baseCapacities_t capacity;
00653 aircraft_t *awayAircraft[MAX_AIRCRAFT];
00654 int numAwayAircraft, randomNum;
00655 aircraft_t *aircraft;
00656
00657 memset(awayAircraft, 0, sizeof(awayAircraft));
00658
00659
00660 capacity = B_GetCapacityFromBuildingType(buildingType);
00661 if (base->capacities[capacity].cur <= base->capacities[capacity].max)
00662 return;
00663
00664 numAwayAircraft = 0;
00665
00666 aircraft = NULL;
00667 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
00668 const int aircraftSize = aircraft->size;
00669 switch (aircraftSize) {
00670 case AIRCRAFT_SMALL:
00671 if (buildingType != B_SMALL_HANGAR)
00672 continue;
00673 break;
00674 case AIRCRAFT_LARGE:
00675 if (buildingType != B_HANGAR)
00676 continue;
00677 break;
00678 default:
00679 Com_Error(ERR_DROP, "B_RemoveAircraftExceedingCapacity: Unknown type of aircraft '%i'", aircraftSize);
00680 }
00681
00684
00685 if (!AIR_IsAircraftInBase(aircraft)) {
00686 if (AIR_IsAircraftOnGeoscape(aircraft))
00687 awayAircraft[numAwayAircraft++] = aircraft;
00688 continue;
00689 }
00690
00691
00692 AIR_DeleteAircraft(aircraft);
00693 awayAircraft[numAwayAircraft++] = NULL;
00694 return;
00695 }
00696
00697 if (!numAwayAircraft)
00698 return;
00699
00700 randomNum = rand() % numAwayAircraft;
00701 if (!CL_DisplayHomebasePopup(awayAircraft[randomNum], qfalse)) {
00702
00704 AIR_DeleteAircraft(awayAircraft[randomNum]);
00705 }
00706 }
00707
00715 qboolean B_BuildingDestroy (base_t* base, building_t* building)
00716 {
00717 const buildingType_t buildingType = building->buildingType;
00718 const building_t const *buildingTemplate = building->tpl;
00719 const qboolean onDestroyCommand = (building->onDestroy[0] != '\0') && (building->buildingStatus == B_STATUS_WORKING);
00720
00721
00722 if (buildingType == B_ENTRANCE)
00723 return qfalse;
00724
00725 if (!base->map[(int)building->pos[0]][(int)building->pos[1]].building
00726 || base->map[(int)building->pos[0]][(int)building->pos[1]].building != building) {
00727 Com_Error(ERR_DROP, "B_BuildingDestroy: building mismatch at base %i pos %i,%i.", base->idx, (int)building->pos[0], (int)building->pos[1]);
00728 }
00729
00730
00731 if (building->needs) {
00732
00733 base->map[(int)building->pos[0]][((int)building->pos[1]) + 1].building = NULL;
00734 }
00735 base->map[(int)building->pos[0]][(int)building->pos[1]].building = NULL;
00736
00737 building->buildingStatus = B_STATUS_NOT_SET;
00738
00739
00740 if (base->buildingCurrent > building)
00741 base->buildingCurrent--;
00742 else if (base->buildingCurrent == building)
00743 base->buildingCurrent = NULL;
00744
00745 {
00746 int const baseIDX = base->idx;
00747 building_t* const buildings = ccs.buildings[baseIDX];
00748 int const idx = building->idx;
00749 int cntBldgs;
00750 int i;
00751
00752 REMOVE_ELEM(buildings, idx, ccs.numBuildings[baseIDX]);
00753
00754
00755 cntBldgs = ccs.numBuildings[baseIDX];
00756 for (i = 0; i < cntBldgs; i++)
00757 if (buildings[i].idx >= idx) {
00758 buildings[i].idx--;
00759 base->map[(int)buildings[i].pos[0]][(int)buildings[i].pos[1]].building = &buildings[i];
00760 if (buildings[i].needs)
00761 base->map[(int)buildings[i].pos[0]][(int)buildings[i].pos[1] + 1].building = &buildings[i];
00762 }
00763
00764 building = NULL;
00765 }
00768 if (buildingType != B_MISC && buildingType != MAX_BUILDING_TYPE) {
00769 if (B_GetNumberOfBuildingsInBaseByBuildingType(base, buildingType) <= 0) {
00770
00771 B_SetBuildingStatus(base, buildingType, qfalse);
00772
00773 B_UpdateStatusBuilding(base, buildingType, qfalse);
00774
00775 B_UpdateBaseCapacities(MAX_CAP, base);
00776 } else {
00777
00778 const baseCapacities_t cap = B_GetCapacityFromBuildingType(buildingType);
00779 if (cap != MAX_CAP)
00780 B_UpdateBaseCapacities(cap, base);
00781 }
00782 }
00783
00784 Cmd_ExecuteString("base_init");
00785
00786
00787
00788 if (onDestroyCommand) {
00789 Com_DPrintf(DEBUG_CLIENT, "B_BuildingDestroy: %s %i %i;\n",
00790 buildingTemplate->onDestroy, base->idx, buildingType);
00791 Cmd_ExecuteString(va("%s %i %i", buildingTemplate->onDestroy, base->idx, buildingType));
00792 }
00793
00794 return qtrue;
00795 }
00796
00802 static void B_MoveAircraftOnGeoscapeToOtherBases (const base_t *base)
00803 {
00804 aircraft_t *aircraft;
00805
00806 aircraft = NULL;
00807 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
00808 if (AIR_IsAircraftOnGeoscape(aircraft)) {
00809 int j;
00810 for (j = 0; j < ccs.numBases; j++) {
00811 base_t *newbase = B_GetBaseByIDX(j);
00812
00813 if (base != newbase && AIR_MoveAircraftIntoNewHomebase(aircraft, newbase))
00814 break;
00815 }
00816 if (j == ccs.numBases) {
00819 AIR_DestroyAircraft(aircraft);
00820 }
00821 }
00822 }
00823 }
00824
00831 void B_Destroy (base_t *base)
00832 {
00833 int buildingIdx;
00834
00835 assert(base);
00836 base->baseStatus = BASE_DESTROYED;
00837
00838 CP_MissionNotifyBaseDestroyed(base);
00839 B_MoveAircraftOnGeoscapeToOtherBases(base);
00840
00841
00842 for (buildingIdx = ccs.numBuildings[base->idx] - 1; buildingIdx >= 0; buildingIdx--) {
00843 building_t *building = &ccs.buildings[base->idx][buildingIdx];
00844 B_BuildingDestroy(base, building);
00845 }
00846
00851 }
00852
00853 #ifdef DEBUG
00854
00858 static void CL_BaseDestroy_f (void)
00859 {
00860 int baseIdx;
00861 base_t *base;
00862
00863 if (Cmd_Argc() < 2) {
00864 Com_Printf("Usage: %s <baseIdx>\n", Cmd_Argv(0));
00865 return;
00866 }
00867
00868 baseIdx = atoi(Cmd_Argv(1));
00869
00870 if (baseIdx < 0 || baseIdx >= MAX_BASES) {
00871 Com_Printf("CL_BaseDestroy_f: baseIdx %i is outside bounds\n", baseIdx);
00872 return;
00873 }
00874
00875 base = B_GetFoundedBaseByIDX(baseIdx);
00876 if (!base) {
00877 Com_Printf("CL_BaseDestroy_f: Base %i not founded\n", baseIdx);
00878 return;
00879 }
00880
00881 B_Destroy(base);
00882 }
00883 #endif
00884
00889 void B_MarkBuildingDestroy (base_t* base, building_t* building)
00890 {
00891 int cap;
00892 assert(base);
00893
00894
00895 if (base->baseStatus == BASE_UNDER_ATTACK) {
00896 UI_Popup(_("Notice"), _("Base is under attack, you can't destroy buildings!"));
00897 return;
00898 }
00899
00900 assert(building);
00901 cap = B_GetCapacityFromBuildingType(building->buildingType);
00902
00903 base->buildingCurrent = building;
00904
00906 if (building->buildingType == B_ENTRANCE) {
00907 UI_Popup(_("Destroy Entrance"), _("You can't destroy the entrance of the base!"));
00908 return;
00909 }
00910
00911 if (building->buildingStatus == B_STATUS_WORKING) {
00912 switch (building->buildingType) {
00913 case B_HANGAR:
00914 case B_SMALL_HANGAR:
00915 if (base->capacities[cap].cur >= base->capacities[cap].max) {
00916 UI_PopupButton(_("Destroy Hangar"), _("If you destroy this hangar, you will also destroy the aircraft inside.\nAre you sure you want to destroy this building?"),
00917 "mn_pop;mn_push aircraft;aircraft_select;", _("Go to hangar"), _("Go to hangar without destroying building"),
00918 "building_destroy;mn_pop;", _("Destroy"), _("Destroy the building"),
00919 (ccs.numBases > 1) ? "mn_pop;mn_push transfer;" : NULL, (ccs.numBases > 1) ? _("Transfer") : NULL,
00920 _("Go to transfer menu without destroying the building"));
00921 return;
00922 }
00923 break;
00924 case B_QUARTERS:
00925 if (base->capacities[cap].cur + building->capacity > base->capacities[cap].max) {
00926 UI_PopupButton(_("Destroy Quarter"), _("If you destroy this Quarters, every employee inside will be killed.\nAre you sure you want to destroy this building?"),
00927 "mn_pop;mn_push employees;employee_list 0;", _("Dismiss"), _("Go to hiring menu without destroying building"),
00928 "building_destroy;mn_pop;", _("Destroy"), _("Destroy the building"),
00929 (ccs.numBases > 1) ? "mn_pop;mn_push transfer;" : NULL, (ccs.numBases > 1) ? _("Transfer") : NULL,
00930 _("Go to transfer menu without destroying the building"));
00931 return;
00932 }
00933 break;
00934 case B_STORAGE:
00935 if (base->capacities[cap].cur + building->capacity > base->capacities[cap].max) {
00936 UI_PopupButton(_("Destroy Storage"), _("If you destroy this Storage, every items inside will be destroyed.\nAre you sure you want to destroy this building?"),
00937 "mn_pop;mn_push market;buy_type *mn_itemtype", _("Go to storage"), _("Go to buy/sell menu without destroying building"),
00938 "building_destroy;mn_pop;", _("Destroy"), _("Destroy the building"),
00939 (ccs.numBases > 1) ? "mn_pop;mn_push transfer;" : NULL, (ccs.numBases > 1) ? _("Transfer") : NULL,
00940 _("Go to transfer menu without destroying the building"));
00941 return;
00942 }
00943 break;
00944 default:
00945 break;
00946 }
00947 }
00948
00949 UI_PopupButton(_("Destroy building"), _("Are you sure you want to destroy this building?"),
00950 NULL, NULL, NULL,
00951 "building_destroy;mn_pop;", _("Destroy"), _("Destroy the building"),
00952 NULL, NULL, NULL);
00953 }
00954
00961 void B_BuildingStatus (const base_t* base, const building_t* building)
00962 {
00963 int numberOfBuildings = 0;
00964
00965 assert(building);
00966 assert(base);
00967
00968 Cvar_Set("mn_building_status", _("Not set"));
00969
00970 switch (building->buildingStatus) {
00971 case B_STATUS_NOT_SET:
00972 numberOfBuildings = B_GetNumberOfBuildingsInBaseByTemplate(base, building->tpl);
00973 if (numberOfBuildings >= 0)
00974 Cvar_Set("mn_building_status", va(_("Already %i in base"), numberOfBuildings));
00975 break;
00976 case B_STATUS_UNDER_CONSTRUCTION:
00977 Cvar_Set("mn_building_status", "");
00978 break;
00979 case B_STATUS_CONSTRUCTION_FINISHED:
00980 Cvar_Set("mn_building_status", _("Construction finished"));
00981 break;
00982 case B_STATUS_WORKING:
00983 if (B_CheckBuildingDependencesStatus(base, building)) {
00984 Cvar_Set("mn_building_status", _("Working 100%"));
00985 } else {
00986 assert (building->dependsBuilding);
00988 Cvar_Set("mn_building_status", va("%s %s", _("Not operational, depends on"), _(building->dependsBuilding->name)));
00989 }
00990 break;
00991 case B_STATUS_DOWN:
00992 Cvar_Set("mn_building_status", _("Down"));
00993 break;
00994 default:
00995 break;
00996 }
00997 }
00998
01007 static void B_UpdateAllBaseBuildingStatus (building_t* building, base_t* base, buildingStatus_t status)
01008 {
01009 qboolean test;
01010 buildingStatus_t oldStatus;
01011
01012 assert(base);
01013 assert(building);
01014
01015 oldStatus = building->buildingStatus;
01016 building->buildingStatus = status;
01017
01018
01019 test = B_CheckUpdateBuilding(building, base);
01020 if (test)
01021 B_UpdateOneBaseBuildingStatusOnEnable(building->buildingType, base);
01022
01023
01024
01025 if (test) {
01026 B_UpdateStatusBuilding(base, building->buildingType, qtrue);
01027
01028 B_UpdateBaseCapacities(MAX_CAP, base);
01029 } else {
01030
01031
01032 const int cap = B_GetCapacityFromBuildingType(building->buildingType);
01033 if (cap != MAX_CAP)
01034 B_UpdateBaseCapacities(cap, base);
01035 }
01036
01038 if (oldStatus == B_STATUS_UNDER_CONSTRUCTION && (status == B_STATUS_CONSTRUCTION_FINISHED || status == B_STATUS_WORKING)) {
01039 if (B_CheckBuildingDependencesStatus(base, building))
01040 CL_GameTimeStop();
01041 } else {
01042 CL_GameTimeStop();
01043 }
01044 }
01045
01053 static void B_AddBuildingToBasePos (base_t *base, const building_t const *buildingTemplate, qboolean hire, const vec2_t pos)
01054 {
01055
01056 building_t *buildingNew;
01057
01058
01059 buildingNew = B_SetBuildingByClick(base, buildingTemplate, (int)pos[0], (int)pos[1]);
01060 B_UpdateAllBaseBuildingStatus(buildingNew, base, B_STATUS_WORKING);
01061 Com_DPrintf(DEBUG_CLIENT, "Base %i new building: %s at (%.0f:%.0f)\n",
01062 base->idx, buildingNew->id, buildingNew->pos[0], buildingNew->pos[1]);
01063
01064 if (hire)
01065 E_HireForBuilding(base, buildingNew, -1);
01066
01067
01068 if (buildingNew->onConstruct[0] != '\0') {
01069 Com_DPrintf(DEBUG_CLIENT, "B_AddBuildingToBasePos: %s %i;\n",
01070 buildingNew->onConstruct, base->idx);
01071 Cmd_ExecuteString(va("%s %i", buildingNew->onConstruct, base->idx));
01072 }
01073 }
01074
01084 static void B_InitialEquipment (base_t *base, aircraft_t *assignInitialAircraft, const char *eqname, equipDef_t *edTarget)
01085 {
01086 int i, price = 0;
01087 const equipDef_t *ed;
01088
01089 assert(base);
01090 assert(edTarget);
01091
01092
01093 ed = INV_GetEquipmentDefinitionByID(eqname);
01094 if (assignInitialAircraft) {
01095 B_PackInitialEquipment(assignInitialAircraft, ed);
01096 } else {
01097 for (i = 0; i < csi.numODs; i++)
01098 edTarget->numItems[i] += ed->numItems[i] / 5;
01099 }
01100
01101
01102 for (i = 0; i < csi.numODs; i++) {
01103 const objDef_t *od = INVSH_GetItemByIDX(i);
01104 price += edTarget->numItems[i] * od->price;
01105 base->capacities[CAP_ITEMS].cur += edTarget->numItems[i] * od->size;
01106 }
01107
01108
01109 CL_UpdateCredits(ccs.credits - price);
01110 }
01111
01120 static void B_BuildFromTemplate (base_t *base, const char *templateName, qboolean hire)
01121 {
01122 const baseTemplate_t *baseTemplate = B_GetBaseTemplate(templateName);
01123 int freeSpace = BASE_SIZE * BASE_SIZE;
01124 int i;
01125
01126 assert(base);
01127
01128 for (i = 0; i < MAX_CAP; i++)
01129 base->capacities[i].cur = 0;
01130
01131 if (baseTemplate) {
01132
01133 for (i = 0; i < baseTemplate->numBuildings; i++) {
01134 vec2_t pos;
01135
01136 Vector2Set(pos, baseTemplate->buildings[i].posX, baseTemplate->buildings[i].posY);
01137
01138 if (!base->map[(int)pos[0]][(int)pos[1]].building) {
01139 B_AddBuildingToBasePos(base, baseTemplate->buildings[i].building, hire, pos);
01140 freeSpace--;
01141 }
01142 }
01143 }
01144
01145
01146 for (i = 0; i < ccs.numBuildingTemplates; i++) {
01147 building_t* building = &ccs.buildingTemplates[i];
01148 vec2_t pos;
01149
01150 if ((!building->mandatory) || B_GetBuildingStatus(base, building->buildingType))
01151 continue;
01152
01153 while ((freeSpace > 0) && !B_GetBuildingStatus(base, building->buildingType)) {
01154 Vector2Set(pos, rand() % BASE_SIZE, rand() % BASE_SIZE);
01155 if (!base->map[(int)pos[0]][(int)pos[1]].building) {
01156 B_AddBuildingToBasePos(base, building, hire, pos);
01157 freeSpace--;
01158 }
01159 }
01162 if (!B_GetBuildingStatus(base, building->buildingType))
01163 Com_Error(ERR_DROP, "B_BuildFromTemplate: Cannot build base. No space for it's buildings!");
01164 }
01165
01166
01167
01168 if (ccs.campaignStats.basesBuilt) {
01169 const int j = round((frand() * (MAX_BLOCKEDFIELDS - MIN_BLOCKEDFIELDS)) + MIN_BLOCKEDFIELDS);
01170
01171 for (i = 0; i < j; i++) {
01172 baseBuildingTile_t *mapPtr = &base->map[rand() % BASE_SIZE][rand() % (BASE_SIZE)];
01173
01174 if (mapPtr->building || mapPtr->blocked)
01175 continue;
01176
01177 mapPtr->blocked = qtrue;
01178 freeSpace--;
01179 }
01180 }
01181
01182 }
01183
01190 static void B_SetUpFirstBase (base_t* base)
01191 {
01192 campaign_t *campaign = ccs.curCampaign;
01193 aircraft_t *aircraft;
01194 const equipDef_t *ed;
01195
01196 if (campaign->firstBaseTemplate[0] == '\0')
01197 Com_Error(ERR_DROP, "No base template for setting up the first base given");
01198
01199 RS_MarkResearchable(qtrue, base);
01200 BS_InitMarket();
01201 E_InitialEmployees();
01202
01203 B_BuildFromTemplate(base, campaign->firstBaseTemplate, qtrue);
01204
01206
01207 if (B_GetBuildingStatus(base, B_HANGAR)) {
01208 const char *firebird = Com_DropShipTypeToShortName(DROPSHIP_FIREBIRD);
01209 const aircraft_t *aircraft = AIR_GetAircraft(firebird);
01210 AIR_NewAircraft(base, firebird);
01211 CL_UpdateCredits(ccs.credits - aircraft->price);
01212 }
01213 if (B_GetBuildingStatus(base, B_SMALL_HANGAR)) {
01214 const char *stiletto = Com_DropShipTypeToShortName(INTERCEPTOR_STILETTO);
01215 const aircraft_t *aircraft = AIR_GetAircraft(stiletto);
01216 AIR_NewAircraft(base, stiletto);
01217 CL_UpdateCredits(ccs.credits - aircraft->price);
01218 }
01219
01220
01221 ed = INV_GetEquipmentDefinitionByID(campaign->equipment);
01222
01223 base->storage = *ed;
01224
01225 aircraft = NULL;
01226 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
01227 if (!E_HireEmployeeByType(base, EMPL_PILOT)) {
01228 Com_DPrintf(DEBUG_CLIENT, "B_SetUpFirstBase: Hiring pilot failed.\n");
01229 }
01230
01231 switch (aircraft->type) {
01232 case AIRCRAFT_INTERCEPTOR:
01233
01234 AIM_AutoEquipAircraft(aircraft);
01235 break;
01236 case AIRCRAFT_TRANSPORTER:
01237
01238 AIR_AssignInitial(aircraft);
01240 B_InitialEquipment(base, aircraft, cp_initial_equipment->string, &base->storage);
01241 break;
01242 default:
01243 Com_Error(ERR_DROP, "B_SetUpFirstBase: Invalid aircraft type.");
01244 }
01245 }
01246 }
01247
01252 int B_GetInstallationLimit (void)
01253 {
01254 int i;
01255 int limit = 0;
01256
01257
01258 for(i = 0; i < ccs.numBases; i++) {
01259 const base_t *base = B_GetFoundedBaseByIDX(i);
01260
01261 limit += (base && B_GetBuildingStatus(base, B_COMMAND)) ? 1 : 0;
01262 }
01263
01264 return min(MAX_INSTALLATIONS, limit * MAX_INSTALLTAIONS_PER_BASE);
01265 }
01266
01270 void B_UpdateBaseCount (void)
01271 {
01272
01273
01274 Cvar_SetValue("mn_base_count", ccs.numBases);
01275 }
01276
01284 void B_SetUpBase (base_t* base, vec2_t pos)
01285 {
01286 const int newBaseAlienInterest = 1.0f;
01287
01288 Vector2Copy(pos, base->pos);
01289
01290 base->idx = B_GetBaseIDX(base);
01291 base->founded = qtrue;
01292 base->baseStatus = BASE_WORKING;
01293 LIST_Delete(&base->aircraft);
01294
01295
01296 if (ccs.campaignStats.basesBuilt == 0)
01297 B_SetUpFirstBase(base);
01298 else
01299 B_BuildFromTemplate(base, NULL, qtrue);
01300
01301
01302 base->alienInterest = newBaseAlienInterest;
01303
01304 BDEF_InitialiseBaseSlots(base);
01305
01306
01307 RADAR_Initialise(&(base->radar), 0.0f, 0.0f, 1.0f, qtrue);
01308 RADAR_InitialiseUFOs(&base->radar);
01309 }
01310
01316 building_t *B_GetBuildingTemplate (const char *buildingName)
01317 {
01318 int i = 0;
01319
01320 assert(buildingName);
01321 for (i = 0; i < ccs.numBuildingTemplates; i++)
01322 if (!strcmp(ccs.buildingTemplates[i].id, buildingName))
01323 return &ccs.buildingTemplates[i];
01324
01325 Com_Printf("Building %s not found\n", buildingName);
01326 return NULL;
01327 }
01328
01334 const baseTemplate_t *B_GetBaseTemplate (const char *baseTemplateID)
01335 {
01336 int i = 0;
01337
01338 if (!baseTemplateID)
01339 return NULL;
01340
01341 for (i = 0; i < ccs.numBaseTemplates; i++)
01342 if (!strcmp(ccs.baseTemplates[i].id, baseTemplateID))
01343 return &ccs.baseTemplates[i];
01344
01345 Com_Printf("Base Template %s not found\n", baseTemplateID);
01346 return NULL;
01347 }
01348
01359 static inline qboolean B_CheckCredits (int costs)
01360 {
01361 if (costs > ccs.credits)
01362 return qfalse;
01363 return qtrue;
01364 }
01365
01377 static qboolean B_ConstructBuilding (base_t* base, building_t *building)
01378 {
01379
01380 if (!base || !building)
01381 return qfalse;
01382
01383
01384 if (!B_CheckCredits(building->fixCosts)) {
01385 Com_DPrintf(DEBUG_CLIENT, "B_ConstructBuilding: Not enough credits to build: '%s'\n", building->id);
01386 B_ResetBuildingCurrent(base);
01387 return qfalse;
01388 }
01389
01390 Com_DPrintf(DEBUG_CLIENT, "Construction of %s is starting\n", building->id);
01391
01392 building->buildingStatus = B_STATUS_UNDER_CONSTRUCTION;
01393 building->timeStart = ccs.date.day;
01394
01395 CL_UpdateCredits(ccs.credits - building->fixCosts);
01396 Cmd_ExecuteString("base_init");
01397 return qtrue;
01398 }
01399
01407 static void B_NewBuilding (base_t* base, building_t *building)
01408 {
01409
01410 if (!base || !building)
01411 return;
01412
01413 if (building->buildingStatus < B_STATUS_UNDER_CONSTRUCTION)
01414
01415 if (B_ConstructBuilding(base, building)) {
01416 B_BuildingStatus(base, building);
01417 Com_DPrintf(DEBUG_CLIENT, "B_NewBuilding: building->buildingStatus = %i\n", building->buildingStatus);
01418 }
01419 }
01420
01429 building_t* B_SetBuildingByClick (base_t *base, const building_t const *buildingTemplate, int row, int col)
01430 {
01431 #ifdef DEBUG
01432 if (!base)
01433 Com_Error(ERR_DROP, "no current base\n");
01434 if (!buildingTemplate)
01435 Com_Error(ERR_DROP, "no current building\n");
01436 #endif
01437 if (!B_CheckCredits(buildingTemplate->fixCosts)) {
01438 UI_Popup(_("Notice"), _("Not enough credits to build this\n"));
01439 return NULL;
01440 }
01441
01442
01443
01444
01445 if (0 <= row && row < BASE_SIZE && 0 <= col && col < BASE_SIZE) {
01446
01447 building_t *buildingNew = &ccs.buildings[base->idx][ccs.numBuildings[base->idx]];
01448
01449
01450 *buildingNew = *buildingTemplate;
01451
01452
01453 buildingNew->idx = B_GetBuildingIDX(base, buildingNew);
01454
01455
01456 buildingNew->base = base;
01457
01458 if (!base->map[row][col].blocked && !base->map[row][col].building) {
01459
01460 if (buildingNew->needs) {
01461 if (col + 1 == BASE_SIZE) {
01462 if (base->map[row][col - 1].building
01463 || base->map[row][col - 1].blocked) {
01464 Com_DPrintf(DEBUG_CLIENT, "Can't place this building here - the second part overlapped with another building or invalid field\n");
01465 return NULL;
01466 }
01467 col--;
01468 } else if (base->map[row][col + 1].building || base->map[row][col + 1].blocked) {
01469 if (base->map[row][col - 1].building
01470 || base->map[row][col - 1].blocked
01471 || !col) {
01472 Com_DPrintf(DEBUG_CLIENT, "Can't place this building here - the second part overlapped with another building or invalid field\n");
01473 return NULL;
01474 }
01475 col--;
01476 }
01477
01478 base->map[row][col + 1].building = buildingNew;
01479 }
01480
01481 ccs.numBuildings[base->idx]++;
01482
01483 B_NewBuilding(base, buildingNew);
01484
01485 base->map[row][col].building = buildingNew;
01486
01487
01488 buildingNew->pos[0] = row;
01489 buildingNew->pos[1] = col;
01490
01491 B_ResetBuildingCurrent(base);
01492 Cmd_ExecuteString("building_init");
01493
01494 return buildingNew;
01495 }
01496 }
01497 return NULL;
01498 }
01499
01500 #define MAX_BUILDING_INFO_TEXT_LENGTH 512
01501
01505 void B_DrawBuilding (base_t* base, building_t* building)
01506 {
01507 static char buildingText[MAX_BUILDING_INFO_TEXT_LENGTH];
01508
01509
01510 if (!base || !building)
01511 return;
01512
01513 buildingText[0] = '\0';
01514
01515 B_BuildingStatus(base, building);
01516
01517 Com_sprintf(buildingText, sizeof(buildingText), "%s\n", _(building->name));
01518
01519 if (building->buildingStatus < B_STATUS_UNDER_CONSTRUCTION && building->fixCosts)
01520 Com_sprintf(buildingText, sizeof(buildingText), _("Costs:\t%i c\n"), building->fixCosts);
01521
01522 if (building->buildingStatus == B_STATUS_UNDER_CONSTRUCTION)
01523 Q_strcat(buildingText, va(ngettext("%i Day to build\n", "%i Days to build\n", building->buildTime), building->buildTime), sizeof(buildingText));
01524
01525 if (building->varCosts)
01526 Q_strcat(buildingText, va(_("Running costs:\t%i c\n"), building->varCosts), sizeof(buildingText));
01527
01528 if (building->dependsBuilding)
01529 Q_strcat(buildingText, va(_("Needs:\t%s\n"), _(building->dependsBuilding->name)), sizeof(buildingText));
01530
01531 if (building->name)
01532 Cvar_Set("mn_building_name", _(building->name));
01533
01534 if (building->image)
01535 Cvar_Set("mn_building_image", building->image);
01536 else
01537 Cvar_Set("mn_building_image", "base/empty");
01538
01539
01540 UI_RegisterText(TEXT_BUILDING_INFO, buildingText);
01541 }
01542
01551 int B_GetNumberOfBuildingsInBaseByTemplate (const base_t *base, const building_t *tpl)
01552 {
01553 int numberOfBuildings = 0;
01554 building_t *building = NULL;
01555
01556 if (!base) {
01557 Com_Printf("B_GetNumberOfBuildingsInBaseByTemplate: No base given!\n");
01558 return -1;
01559 }
01560
01561 if (!tpl) {
01562 Com_Printf("B_GetNumberOfBuildingsInBaseByTemplate: no building-type given!\n");
01563 return -1;
01564 }
01565
01566
01567 if (tpl != tpl->tpl) {
01568 Com_Printf("B_GetNumberOfBuildingsInBaseByTemplate: No building-type given as parameter. It's probably a normal building!\n");
01569 return -1;
01570 }
01571
01572 while ((building = B_GetNextBuilding(base, building))) {
01573 if (building->tpl == tpl && building->buildingStatus != B_STATUS_NOT_SET)
01574 numberOfBuildings++;
01575 }
01576 return numberOfBuildings;
01577 }
01578
01587 int B_GetNumberOfBuildingsInBaseByBuildingType (const base_t *base, const buildingType_t buildingType)
01588 {
01589 int numberOfBuildings = 0;
01590 building_t *building = NULL;
01591
01592 if (!base) {
01593 Com_Printf("B_GetNumberOfBuildingsInBaseByBuildingType: No base given!\n");
01594 return -1;
01595 }
01596
01597 if (buildingType >= MAX_BUILDING_TYPE) {
01598 Com_Printf("B_GetNumberOfBuildingsInBaseByBuildingType: no sane building-type given!\n");
01599 return -1;
01600 }
01601
01602 while ((building = B_GetNextBuildingByType(base, building, buildingType)))
01603 if (building->buildingStatus != B_STATUS_NOT_SET)
01604 numberOfBuildings++;
01605
01606 return numberOfBuildings;
01607 }
01608
01616 buildingType_t B_GetBuildingTypeByBuildingID (const char *buildingID)
01617 {
01618 if (!strcmp(buildingID, "lab")) {
01619 return B_LAB;
01620 } else if (!strcmp(buildingID, "hospital")) {
01621 return B_HOSPITAL;
01622 } else if (!strcmp(buildingID, "aliencont")) {
01623 return B_ALIEN_CONTAINMENT;
01624 } else if (!strcmp(buildingID, "workshop")) {
01625 return B_WORKSHOP;
01626 } else if (!strcmp(buildingID, "storage")) {
01627 return B_STORAGE;
01628 } else if (!strcmp(buildingID, "hangar")) {
01629 return B_HANGAR;
01630 } else if (!strcmp(buildingID, "smallhangar")) {
01631 return B_SMALL_HANGAR;
01632 } else if (!strcmp(buildingID, "quarters")) {
01633 return B_QUARTERS;
01634 } else if (!strcmp(buildingID, "workshop")) {
01635 return B_WORKSHOP;
01636 } else if (!strcmp(buildingID, "power")) {
01637 return B_POWER;
01638 } else if (!strcmp(buildingID, "command")) {
01639 return B_COMMAND;
01640 } else if (!strcmp(buildingID, "amstorage")) {
01641 return B_ANTIMATTER;
01642 } else if (!strcmp(buildingID, "entrance")) {
01643 return B_ENTRANCE;
01644 } else if (!strcmp(buildingID, "missile")) {
01645 return B_DEFENCE_MISSILE;
01646 } else if (!strcmp(buildingID, "laser")) {
01647 return B_DEFENCE_LASER;
01648 } else if (!strcmp(buildingID, "radar")) {
01649 return B_RADAR;
01650 }
01651 return MAX_BUILDING_TYPE;
01652 }
01653
01665 void B_ParseBuildings (const char *name, const char **text, qboolean link)
01666 {
01667 building_t *building;
01668 building_t *dependsBuilding;
01669 technology_t *tech_link;
01670 const value_t *vp;
01671 const char *errhead = "B_ParseBuildings: unexpected end of file (names ";
01672 const char *token;
01673
01674
01675 token = Com_Parse(text);
01676 if (!*text || *token != '{') {
01677 Com_Printf("B_ParseBuildings: building \"%s\" without body ignored\n", name);
01678 return;
01679 }
01680
01681 if (ccs.numBuildingTemplates >= MAX_BUILDINGS)
01682 Com_Error(ERR_DROP, "B_ParseBuildings: too many buildings");
01683
01684 if (!link) {
01685 int i;
01686 for (i = 0; i < ccs.numBuildingTemplates; i++) {
01687 if (!strcmp(ccs.buildingTemplates[i].id, name)) {
01688 Com_Printf("B_ParseBuildings: Second building with same name found (%s) - second ignored\n", name);
01689 return;
01690 }
01691 }
01692
01693
01694 building = &ccs.buildingTemplates[ccs.numBuildingTemplates];
01695 memset(building, 0, sizeof(*building));
01696 building->id = Mem_PoolStrDup(name, cp_campaignPool, 0);
01697
01698 Com_DPrintf(DEBUG_CLIENT, "...found building %s\n", building->id);
01699
01700
01701 building->tpl = building;
01702 building->idx = -1;
01703 building->base = NULL;
01704 building->buildingType = MAX_BUILDING_TYPE;
01705 building->dependsBuilding = NULL;
01706 building->visible = qtrue;
01707 building->maxCount = -1;
01708
01709 ccs.numBuildingTemplates++;
01710 do {
01711
01712 token = Com_EParse(text, errhead, name);
01713 if (!*text)
01714 break;
01715 if (*token == '}')
01716 break;
01717
01718
01719 if (!strcmp(token, "type")) {
01720 token = Com_EParse(text, errhead, name);
01721 if (!*text)
01722 return;
01723
01724 building->buildingType = B_GetBuildingTypeByBuildingID(token);
01725 if (building->buildingType >= MAX_BUILDING_TYPE)
01726 Com_Printf("didn't find buildingType '%s'\n", token);
01727 } else {
01728
01729 if (!strcmp(token, "depends")) {
01730 token = Com_EParse(text, errhead, name);
01731 if (!*text)
01732 return;
01733 } else {
01734 for (vp = valid_building_vars; vp->string; vp++)
01735 if (!strncmp(token, vp->string, sizeof(vp->string))) {
01736
01737 token = Com_EParse(text, errhead, name);
01738 if (!*text)
01739 return;
01740
01741 switch (vp->type) {
01742 case V_NULL:
01743 break;
01744 case V_TRANSLATION_STRING:
01745 token++;
01746 case V_CLIENT_HUNK_STRING:
01747 Mem_PoolStrDupTo(token, (char**) ((char*)building + (int)vp->ofs), cp_campaignPool, 0);
01748 break;
01749 default:
01750 Com_EParseValue(building, token, vp->type, vp->ofs, vp->size);
01751 break;
01752 }
01753 break;
01754 }
01755 if (!vp->string)
01756 Com_Printf("B_ParseBuildings: unknown token \"%s\" ignored (building %s)\n", token, name);
01757 }
01758 }
01759 } while (*text);
01760 } else {
01761 building = B_GetBuildingTemplate(name);
01762 if (!building)
01763 Com_Error(ERR_DROP, "B_ParseBuildings: Could not find building with id %s\n", name);
01764
01765 tech_link = RS_GetTechByProvided(name);
01766 if (tech_link)
01767 building->tech = tech_link;
01768 else if (building->visible)
01769 Com_Error(ERR_DROP, "B_ParseBuildings: Could not find tech that provides %s\n", name);
01770
01771 do {
01772
01773 token = Com_EParse(text, errhead, name);
01774 if (!*text)
01775 break;
01776 if (*token == '}')
01777 break;
01778
01779 if (!strcmp(token, "depends")) {
01780 dependsBuilding = B_GetBuildingTemplate(Com_EParse(text, errhead, name));
01781 if (!dependsBuilding)
01782 Com_Error(ERR_DROP, "Could not find building depend of %s\n", building->id);
01783 building->dependsBuilding = dependsBuilding;
01784 if (!*text)
01785 return;
01786 }
01787 } while (*text);
01788 }
01789 }
01790
01798 building_t *B_GetBuildingInBaseByType (const base_t* base, buildingType_t buildingType, qboolean onlyWorking)
01799 {
01800 building_t *building = NULL;
01801
01802
01803
01804
01805 if (onlyWorking && !B_GetBuildingStatus(base, buildingType))
01806 return NULL;
01807
01808 while ((building = B_GetNextBuildingByType(base, building, buildingType)))
01809 return building;
01810
01811 return NULL;
01812 }
01813
01818 void B_ParseBaseTemplate (const char *name, const char **text)
01819 {
01820 const char *errhead = "B_ParseBaseTemplate: unexpected end of file (names ";
01821 const char *token;
01822 baseTemplate_t* baseTemplate;
01823 baseBuildingTile_t* tile;
01824 vec2_t pos;
01825 qboolean map[BASE_SIZE][BASE_SIZE];
01826 byte buildingNums[MAX_BUILDINGS];
01827 int i;
01828
01829
01830 token = Com_Parse(text);
01831
01832 if (!*text || *token != '{') {
01833 Com_Printf("B_ParseBaseTemplate: Template \"%s\" without body ignored\n", name);
01834 return;
01835 }
01836
01837 if (ccs.numBaseTemplates >= MAX_BASETEMPLATES)
01838 Com_Error(ERR_DROP, "B_ParseBaseTemplate: too many base templates");
01839
01840
01841 baseTemplate = &ccs.baseTemplates[ccs.numBaseTemplates];
01842 baseTemplate->id = Mem_PoolStrDup(name, cp_campaignPool, 0);
01843
01844
01845 memset(&map, 0, sizeof(map));
01846 memset(&buildingNums, 0, sizeof(buildingNums));
01847
01848 ccs.numBaseTemplates++;
01849
01850 do {
01851
01852 token = Com_EParse(text, errhead, baseTemplate->id);
01853 if (!*text)
01854 break;
01855 if (*token == '}')
01856 break;
01857
01858 if (baseTemplate->numBuildings >= MAX_BASEBUILDINGS)
01859 Com_Error(ERR_DROP, "B_ParseBaseTemplate: too many buildings");
01860
01861
01862 tile = &baseTemplate->buildings[baseTemplate->numBuildings];
01863 baseTemplate->numBuildings++;
01864
01865 for (i = 0; i < ccs.numBuildingTemplates; i++)
01866 if (!strcmp(ccs.buildingTemplates[i].id, token)) {
01867 tile->building = &ccs.buildingTemplates[i];
01868 if (tile->building->maxCount >= 0 && tile->building->maxCount <= buildingNums[i])
01869 Com_Error(ERR_DROP, "B_ParseBaseTemplate: Found more %s than allowed in template %s (%d))", token, baseTemplate->id, tile->building->maxCount);
01870 buildingNums[i]++;
01871 break;
01872 }
01873
01874 if (!tile->building)
01875 Com_Error(ERR_DROP, "B_ParseBaseTemplate: Could not find building with id %s\n", baseTemplate->id);
01876
01877
01878 token = Com_EParse(text, errhead, baseTemplate->id);
01879 if (!*text)
01880 break;
01881 if (*token == '}')
01882 break;
01883
01884 Com_EParseValue(pos, token, V_POS, 0, sizeof(vec2_t));
01885 tile->posX = pos[0];
01886 tile->posY = pos[1];
01887 if (tile->posX < 0 || tile->posX >= BASE_SIZE || tile->posY < 0 || tile->posY >= BASE_SIZE)
01888 Com_Error(ERR_DROP, "Invalid template coordinates for building %s in template %s given",
01889 tile->building->id, baseTemplate->id);
01890
01891
01892 if (map[tile->posX][tile->posY])
01893 Com_Error(ERR_DROP, "Base template '%s' has ambiguous positions for buildings set.", baseTemplate->id);
01894 map[tile->posX][tile->posY] = qtrue;
01895 } while (*text);
01896
01897
01898 for (i = 0; i < ccs.numBuildingTemplates; i++) {
01899 const building_t *building = &ccs.buildingTemplates[i];
01900 if (building && building->mandatory && !buildingNums[i]) {
01901 Com_Error(ERR_DROP, "Every base template needs one '%s'! '%s' has none.", building->id, baseTemplate->id);
01902 }
01903 }
01904 }
01905
01910 base_t *B_GetFirstUnfoundedBase (void)
01911 {
01912 int baseIdx;
01913
01914 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
01915 base_t *base = B_GetBaseByIDX(baseIdx);
01916 if (!base->founded)
01917 return base;
01918 }
01919
01920 return NULL;
01921 }
01922
01927 void B_SetCurrentSelectedBase (const base_t *base)
01928 {
01929 int i;
01930
01931 for (i = 0; i < MAX_BASES; i++) {
01932 base_t *b = B_GetBaseByIDX(i);
01933 if (b == base) {
01934 b->selected = qtrue;
01935 if (!b->founded)
01936 Com_Error(ERR_DROP, "The base you are trying to select is not founded yet");
01937 if (b->aircraftCurrent == NULL)
01938 b->aircraftCurrent = AIR_GetNextFromBase(b, NULL);
01939 } else
01940 b->selected = qfalse;
01941 }
01942
01943 if (base) {
01944 INS_SetCurrentSelectedInstallation(NULL);
01945 Cvar_Set("mn_base_title", base->name);
01946 Cvar_SetValue("mn_base_status_id", base->baseStatus);
01947 } else {
01948 Cvar_Set("mn_base_title", "");
01949 Cvar_Set("mn_base_status_id", "");
01950 }
01951 }
01952
01953 base_t *B_GetCurrentSelectedBase (void)
01954 {
01955 int i;
01956
01957 for (i = 0; i < MAX_BASES; i++) {
01958 base_t *base = B_GetBaseByIDX(i);
01959 if (base->selected)
01960 return base;
01961 }
01962
01963 return NULL;
01964 }
01965
01969 void B_SelectBase (const base_t *base)
01970 {
01971
01972 if (!base) {
01973
01974
01975 if (ccs.mapAction == MA_NEWBASE) {
01976 MAP_ResetAction();
01977 return;
01978 }
01979
01980 if (ccs.numBases < MAX_BASES) {
01981
01982 if (!MAP_IsRadarOverlayActivated())
01983 MAP_SetOverlay("radar");
01984 ccs.mapAction = MA_NEWBASE;
01985 } else {
01986 ccs.mapAction = MA_NONE;
01987 }
01988 } else {
01989 Com_DPrintf(DEBUG_CLIENT, "B_SelectBase_f: select base with id %i\n", base->idx);
01990 ccs.mapAction = MA_NONE;
01991 UI_PushWindow("bases", NULL);
01992 B_SetCurrentSelectedBase(base);
01993 }
01994 }
01995
01999 static void CL_SwapSkill (character_t *cp1, character_t *cp2, abilityskills_t skill)
02000 {
02001 int tmp1, tmp2;
02002 tmp1 = cp1->score.skills[skill];
02003 tmp2 = cp2->score.skills[skill];
02004 cp1->score.skills[skill] = tmp2;
02005 cp2->score.skills[skill] = tmp1;
02006
02007 tmp1 = cp1->score.initialSkills[skill];
02008 tmp2 = cp2->score.initialSkills[skill];
02009 cp1->score.initialSkills[skill] = tmp2;
02010 cp2->score.initialSkills[skill] = tmp1;
02011
02012 tmp1 = cp1->score.experience[skill];
02013 tmp2 = cp2->score.experience[skill];
02014 cp1->score.experience[skill] = tmp2;
02015 cp2->score.experience[skill] = tmp1;
02016 }
02017
02018 static void CL_DoSwapSkills (character_t *cp1, character_t *cp2, const int skill)
02019 {
02020 if (cp1->score.skills[skill] < cp2->score.skills[skill])
02021 CL_SwapSkill(cp1, cp2, skill);
02022
02023 switch (skill) {
02024 case SKILL_CLOSE:
02025 if (cp1->score.skills[ABILITY_SPEED] < cp2->score.skills[ABILITY_SPEED])
02026 CL_SwapSkill(cp1, cp2, ABILITY_SPEED);
02027 break;
02028 case SKILL_HEAVY:
02029 if (cp1->score.skills[ABILITY_POWER] < cp2->score.skills[ABILITY_POWER])
02030 CL_SwapSkill(cp1, cp2, ABILITY_POWER);
02031 break;
02032 case SKILL_ASSAULT:
02033
02034 break;
02035 case SKILL_SNIPER:
02036 if (cp1->score.skills[ABILITY_ACCURACY] < cp2->score.skills[ABILITY_ACCURACY])
02037 CL_SwapSkill(cp1, cp2, ABILITY_ACCURACY);
02038 break;
02039 case SKILL_EXPLOSIVE:
02040 if (cp1->score.skills[ABILITY_MIND] < cp2->score.skills[ABILITY_MIND])
02041 CL_SwapSkill(cp1, cp2, ABILITY_MIND);
02042 break;
02043 default:
02044 Com_Error(ERR_DROP, "CL_SwapSkills: illegal skill %i.\n", skill);
02045 }
02046 }
02047
02053 static void CL_SwapSkills (chrList_t *team)
02054 {
02055 int i, j, k, skill;
02056 const byte fmode1 = 0;
02057 const byte fmode2 = 1;
02058
02059 i = team->num;
02060 while (i--) {
02061
02062
02063
02064 for (skill = ABILITY_NUM_TYPES; skill < SKILL_NUM_TYPES; skill++) {
02065 for (j = 0; j < team->num - 1; j++) {
02066 character_t *cp1 = team->chr[j];
02067 const fireDef_t *fdRightArray = NULL;
02068 const fireDef_t *fdHolsterArray = NULL;
02069
02070 if (RIGHT(cp1) && RIGHT(cp1)->item.m && RIGHT(cp1)->item.t)
02071 fdRightArray = FIRESH_FiredefForWeapon(&RIGHT(cp1)->item);
02072 if (HOLSTER(cp1) && HOLSTER(cp1)->item.m && HOLSTER(cp1)->item.t)
02073 fdHolsterArray = FIRESH_FiredefForWeapon(&HOLSTER(cp1)->item);
02074
02075
02076 if (fdHolsterArray != NULL && fdRightArray != NULL) {
02077 const int no1 = 2 * (RIGHT(cp1) && skill == RIGHT(cp1)->item.m->fd[fdRightArray->weapFdsIdx][fmode1].weaponSkill)
02078 + 2 * (RIGHT(cp1) && skill == RIGHT(cp1)->item.m->fd[fdRightArray->weapFdsIdx][fmode2].weaponSkill)
02079 + (HOLSTER(cp1) && HOLSTER(cp1)->item.t->reload
02080 && skill == HOLSTER(cp1)->item.m->fd[fdHolsterArray->weapFdsIdx][fmode1].weaponSkill)
02081 + (HOLSTER(cp1) && HOLSTER(cp1)->item.t->reload
02082 && skill == HOLSTER(cp1)->item.m->fd[fdHolsterArray->weapFdsIdx][fmode2].weaponSkill);
02083
02084 for (k = j + 1; k < team->num; k++) {
02085 character_t *cp2 = team->chr[k];
02086 fdRightArray = NULL;
02087 fdHolsterArray = NULL;
02088
02089 if (RIGHT(cp2) && RIGHT(cp2)->item.m && RIGHT(cp2)->item.t)
02090 fdRightArray = FIRESH_FiredefForWeapon(&RIGHT(cp2)->item);
02091 if (HOLSTER(cp2) && HOLSTER(cp2)->item.m && HOLSTER(cp2)->item.t)
02092 fdHolsterArray = FIRESH_FiredefForWeapon(&HOLSTER(cp2)->item);
02093
02094 if (fdHolsterArray != NULL && fdRightArray != NULL) {
02095 const int no2 = 2 * (RIGHT(cp2) && skill == RIGHT(cp2)->item.m->fd[fdRightArray->weapFdsIdx][fmode1].weaponSkill)
02096 + 2 * (RIGHT(cp2) && skill == RIGHT(cp2)->item.m->fd[fdRightArray->weapFdsIdx][fmode2].weaponSkill)
02097 + (HOLSTER(cp2) && HOLSTER(cp2)->item.t->reload
02098 && skill == HOLSTER(cp2)->item.m->fd[fdHolsterArray->weapFdsIdx][fmode1].weaponSkill)
02099 + (HOLSTER(cp2) && HOLSTER(cp2)->item.t->reload
02100 && skill == HOLSTER(cp2)->item.m->fd[fdHolsterArray->weapFdsIdx][fmode2].weaponSkill);
02101
02102 if (no1 > no2
02103 || (no1 && no1 == no2)) {
02104
02105 CL_DoSwapSkills(cp1, cp2, skill);
02106 } else if (no1 < no2) {
02107 CL_DoSwapSkills(cp2, cp1, skill);
02108 }
02109 }
02110 }
02111 }
02112 }
02113 }
02114 }
02115 }
02116
02121 static void B_PackInitialEquipment (aircraft_t *aircraft, const equipDef_t *ed)
02122 {
02123 base_t *base = aircraft->homebase;
02124 chrList_t chrListTemp;
02125 linkedList_t* l;
02126
02127 if (!aircraft)
02128 return;
02129
02130 chrListTemp.num = 0;
02131 for (l = aircraft->acTeam; l != NULL; l = l->next) {
02132 employee_t *employee = (employee_t *)l->data;
02133 character_t *chr = &employee->chr;
02134
02135 Com_DPrintf(DEBUG_CLIENT, "B_PackInitialEquipment: Packing initial equipment for %s.\n", chr->name);
02136 cls.i.EquipActor(&cls.i, &chr->i, ed, chr->teamDef);
02137 chrListTemp.chr[chrListTemp.num] = chr;
02138 chrListTemp.num++;
02139 }
02140
02141 if (base) {
02142 AIR_MoveEmployeeInventoryIntoStorage(aircraft, &base->storage);
02143 B_UpdateStorageCap(base);
02144 }
02145 CL_SwapSkills(&chrListTemp);
02146 }
02147
02153 void B_BaseResetStatus (base_t* const base)
02154 {
02155 assert(base);
02156 base->baseStatus = BASE_NOT_USED;
02157 if (ccs.mapAction == MA_BASEATTACK)
02158 ccs.mapAction = MA_NONE;
02159 }
02160
02161 #ifdef DEBUG
02162
02167 static void B_BuildingList_f (void)
02168 {
02169 int baseIdx, j, k;
02170 building_t *building;
02171
02172 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
02173 const base_t const *base = B_GetFoundedBaseByIDX(baseIdx);
02174 if (!base)
02175 continue;
02176
02177 building = &ccs.buildings[base->idx][baseIdx];
02178 Com_Printf("\nBase id %i: %s\n", baseIdx, base->name);
02179 for (j = 0; j < ccs.numBuildings[baseIdx]; j++) {
02180 Com_Printf("...Building: %s #%i - id: %i\n", building->id,
02181 B_GetNumberOfBuildingsInBaseByTemplate(base, building->tpl), baseIdx);
02182 Com_Printf("...image: %s\n", building->image);
02183 Com_Printf(".....Status:\n");
02184 for (k = 0; k < BASE_SIZE * BASE_SIZE; k++) {
02185 if (k > 1 && k % BASE_SIZE == 0)
02186 Com_Printf("\n");
02187 Com_Printf("%i ", building->buildingStatus);
02188 if (!building->buildingStatus)
02189 break;
02190 }
02191 Com_Printf("\n");
02192 building++;
02193 }
02194 }
02195 }
02196
02202 static void B_BaseList_f (void)
02203 {
02204 int i, row, col, j;
02205 base_t *base;
02206
02207 for (i = 0, base = ccs.bases; i < MAX_BASES; i++, base++) {
02208 aircraft_t *aircraft = NULL;
02209 if (!base->founded) {
02210 Com_Printf("Base idx %i not founded\n\n", i);
02211 continue;
02212 }
02213
02214 Com_Printf("Base idx %i\n", base->idx);
02215 Com_Printf("Base name %s\n", base->name);
02216 Com_Printf("Base founded %i\n", base->founded);
02217 Com_Printf("Base numMissileBattery %i\n", base->numBatteries);
02218 Com_Printf("Base numLaserBattery %i\n", base->numLasers);
02219 Com_Printf("Base radarRange %i\n", base->radar.range);
02220 Com_Printf("Base trackingRange %i\n", base->radar.trackingRange);
02221 Com_Printf("Base numSensoredAircraft %i\n", base->radar.numUFOs);
02222 Com_Printf("Base Alien interest %f\n", base->alienInterest);
02223 Com_Printf("Base hasBuilding[]:\n");
02224 Com_Printf("Misc Lab Quar Stor Work Hosp Hang Cont SHgr UHgr SUHg Powr Cmd AMtr Entr Miss Lasr Rdr Team\n");
02225 for (j = 0; j < MAX_BUILDING_TYPE; j++)
02226 Com_Printf(" %i ", B_GetBuildingStatus(base, j));
02227 Com_Printf("\nBase aircraft %i\n", LIST_Count(base->aircraft));
02228 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL)
02229 Com_Printf("Base aircraft-team %i\n", AIR_GetTeamSize(aircraft));
02230 Com_Printf("Base pos %.02f:%.02f\n", base->pos[0], base->pos[1]);
02231 Com_Printf("Base map:\n");
02232 for (row = 0; row < BASE_SIZE; row++) {
02233 if (row)
02234 Com_Printf("\n");
02235 for (col = 0; col < BASE_SIZE; col++)
02236 Com_Printf("%2i (%3i: %3i) ", (base->map[row][col].building ? base->map[row][col].building->idx : -1),
02237 base->map[row][col].posX, base->map[row][col].posY);
02238 }
02239 Com_Printf("\n\n");
02240 }
02241 }
02242 #endif
02243
02250 void B_BuildingOpenAfterClick (const base_t *base, const building_t *building)
02251 {
02252 assert(base);
02253 assert(building);
02254 if (!B_GetBuildingStatus(base, building->buildingType)) {
02255 UP_OpenWith(building->pedia);
02256 } else {
02257 switch (building->buildingType) {
02258 case B_LAB:
02259 if (RS_ResearchAllowed(base))
02260 UI_PushWindow("research", NULL);
02261 else
02262 UP_OpenWith(building->pedia);
02263 break;
02264 case B_HOSPITAL:
02265 if (HOS_HospitalAllowed(base))
02266 UI_PushWindow("hospital", NULL);
02267 else
02268 UP_OpenWith(building->pedia);
02269 break;
02270 case B_ALIEN_CONTAINMENT:
02271 if (AC_ContainmentAllowed(base))
02272 UI_PushWindow("aliencont", NULL);
02273 else
02274 UP_OpenWith(building->pedia);
02275 break;
02276 case B_QUARTERS:
02277 if (E_HireAllowed(base))
02278 UI_PushWindow("employees", NULL);
02279 else
02280 UP_OpenWith(building->pedia);
02281 break;
02282 case B_WORKSHOP:
02283 if (PR_ProductionAllowed(base))
02284 UI_PushWindow("production", NULL);
02285 else
02286 UP_OpenWith(building->pedia);
02287 break;
02288 case B_DEFENCE_LASER:
02289 case B_DEFENCE_MISSILE:
02290 UI_PushWindow("basedefence", NULL);
02291 break;
02292 case B_HANGAR:
02293 case B_SMALL_HANGAR:
02294 if (!AIR_AircraftAllowed(base)) {
02295 UP_OpenWith(building->pedia);
02296 } else if (AIR_BaseHasAircraft(base)) {
02297 UI_PushWindow("aircraft", NULL);
02298 } else {
02299 UI_PushWindow("buyaircraft", NULL);
02300
02301 if (ccs.numBases > 1)
02302 UI_Popup(_("Note"), _("No aircraft in this base - You first have to purchase or transfer an aircraft\n"));
02303 else
02304 UI_Popup(_("Note"), _("No aircraft in this base - You first have to purchase an aircraft\n"));
02305 }
02306 break;
02307 case B_STORAGE:
02308 if (BS_BuySellAllowed(base))
02309 UI_PushWindow("market", NULL);
02310 else
02311 UP_OpenWith(building->pedia);
02312 break;
02313 case B_ANTIMATTER:
02314 Com_sprintf(popupText, sizeof(popupText), "%s %d/%d", _("Antimatter (current/max):"), base->capacities[CAP_ANTIMATTER].cur, base->capacities[CAP_ANTIMATTER].max);
02315 UI_Popup(_("Information"), popupText);
02316 break;
02317 default:
02318 UP_OpenWith(building->pedia);
02319 break;
02320 }
02321 }
02322 }
02323
02324 #ifdef DEBUG
02325
02329 static void B_PrintCapacities_f (void)
02330 {
02331 int i, j;
02332 base_t *base;
02333
02334 if (Cmd_Argc() < 2) {
02335 Com_Printf("Usage: %s <baseID>\n", Cmd_Argv(0));
02336 return;
02337 }
02338
02339 i = atoi(Cmd_Argv(1));
02340 if (i >= ccs.numBases) {
02341 Com_Printf("invalid baseID (%s)\n", Cmd_Argv(1));
02342 return;
02343 }
02344 base = B_GetBaseByIDX(i);
02345 for (i = 0; i < MAX_CAP; i++) {
02346 const buildingType_t buildingType = B_GetBuildingTypeByCapacity(i);
02347 if (buildingType >= MAX_BUILDING_TYPE)
02348 Com_Printf("B_PrintCapacities_f: Could not find building associated with capacity %i\n", i);
02349 else {
02350 for (j = 0; j < ccs.numBuildingTemplates; j++) {
02351 if (ccs.buildingTemplates[j].buildingType == buildingType)
02352 break;
02353 }
02354 Com_Printf("Building: %s, capacity max: %i, capacity cur: %i\n",
02355 ccs.buildingTemplates[j].id, base->capacities[i].max, base->capacities[i].cur);
02356 }
02357 }
02358 }
02359
02363 static void B_BuildingConstructionFinished_f (void)
02364 {
02365 int i;
02366 base_t *base = B_GetCurrentSelectedBase();
02367
02368 if (!base)
02369 return;
02370
02371 for (i = 0; i < ccs.numBuildings[base->idx]; i++) {
02372 building_t *building = &ccs.buildings[base->idx][i];
02373
02374 if (building->buildingStatus == B_STATUS_UNDER_CONSTRUCTION) {
02375 B_UpdateAllBaseBuildingStatus(building, base, B_STATUS_WORKING);
02376
02377 if (building->onConstruct[0] != '\0') {
02378 base->buildingCurrent = building;
02379 Cbuf_AddText(va("%s %i;", building->onConstruct, base->idx));
02380 }
02381 }
02382 }
02383
02384 B_SelectBase(base);
02385 }
02386
02391 static void B_ResetAllStatusAndCapacities_f (void)
02392 {
02393 int baseIdx;
02394
02395 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
02396 base_t *base = B_GetFoundedBaseByIDX(baseIdx);
02397 if (!base)
02398 continue;
02399
02400
02401 B_ResetAllStatusAndCapacities(base, qfalse);
02402 }
02403 }
02404 #endif
02405
02410 void B_InitStartup (void)
02411 {
02412 #ifdef DEBUG
02413 Cmd_AddCommand("debug_listbase", B_BaseList_f, "Print base information to the game console");
02414 Cmd_AddCommand("debug_listbuilding", B_BuildingList_f, "Print building information to the game console");
02415 Cmd_AddCommand("debug_listcapacities", B_PrintCapacities_f, "Debug function to show all capacities in given base");
02416 Cmd_AddCommand("debug_basereset", B_ResetAllStatusAndCapacities_f, "Reset building status and capacities of all bases");
02417 Cmd_AddCommand("debug_destroybase", CL_BaseDestroy_f, "Destroy a base");
02418 Cmd_AddCommand("debug_buildingfinished", B_BuildingConstructionFinished_f, "Finish construction for every building in the current base");
02419 #endif
02420
02421 cp_initial_equipment = Cvar_Get("cp_initial_equipment", "phalanx_initial", 0, "Start with assigned equipment - see cp_start_employees");
02422 }
02423
02428 int B_GetFoundedBaseCount (void)
02429 {
02430 int i, cnt = 0;
02431
02432 for (i = 0; i < MAX_BASES; i++) {
02433 if (!ccs.bases[i].founded)
02434 continue;
02435 cnt++;
02436 }
02437
02438 return cnt;
02439 }
02440
02447 void B_UpdateBaseData (void)
02448 {
02449 int baseIdx;
02450
02451 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
02452 base_t *base = B_GetFoundedBaseByIDX(baseIdx);
02453 building_t *b;
02454 if (!base)
02455 continue;
02456
02457 b = NULL;
02458 while ((b = B_GetNextBuilding(base, b))) {
02459 if (B_CheckBuildingConstruction(b, base)) {
02460 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("Construction of %s building finished in %s."), _(b->name), ccs.bases[baseIdx].name);
02461 MS_AddNewMessage(_("Building finished"), cp_messageBuffer, qfalse, MSG_CONSTRUCTION, NULL);
02462 }
02463 }
02464 }
02465 }
02466
02471 int B_CheckBuildingConstruction (building_t *building, base_t *base)
02472 {
02473 int newBuilding = 0;
02474
02475 if (building->buildingStatus == B_STATUS_UNDER_CONSTRUCTION) {
02476 if (building->timeStart && building->timeStart + building->buildTime <= ccs.date.day) {
02477 B_UpdateAllBaseBuildingStatus(building, base, B_STATUS_WORKING);
02478
02479 if (building->onConstruct[0] != '\0') {
02480 base->buildingCurrent = building;
02481 Com_DPrintf(DEBUG_CLIENT, "B_CheckBuildingConstruction: %s %i;\n", building->onConstruct, base->idx);
02482 Cbuf_AddText(va("%s %i;", building->onConstruct, base->idx));
02483 }
02484
02485 newBuilding++;
02486 }
02487 }
02488
02489 if (newBuilding)
02490 Cmd_ExecuteString("building_init");
02491
02492 return newBuilding;
02493 }
02494
02500 static void B_SellOrAddItems (aircraft_t *aircraft)
02501 {
02502 int i;
02503 int numitems = 0;
02504 int gained = 0;
02505 int forcedsold = 0;
02506 int forcedgained = 0;
02507 itemsTmp_t *cargo;
02508 base_t *base;
02509
02510 assert(aircraft);
02511 base = aircraft->homebase;
02512 assert(base);
02513
02514 cargo = aircraft->itemcargo;
02515
02516 for (i = 0; i < aircraft->itemTypes; i++) {
02517 const objDef_t *item = cargo[i].item;
02518 const int amount = cargo[i].amount;
02519 technology_t *tech = RS_GetTechForItem(item);
02520
02521 if (!RS_IsResearched_ptr(tech)) {
02522
02523 B_UpdateStorageAndCapacity(base, item, amount, qfalse, qtrue);
02524 if (amount > 0)
02525 RS_MarkCollected(tech);
02526 continue;
02527 } else {
02528
02529 if (ccs.eMarket.autosell[item->idx]) {
02530 BS_AddItemToMarket(item, amount);
02531 gained += (item->price * amount);
02532 numitems += amount;
02533 } else {
02534 int j;
02535
02536
02537 for (j = 0; j < amount; j++) {
02538 if (!B_UpdateStorageAndCapacity(base, item, 1, qfalse, qfalse)) {
02539
02540 BS_AddItemToMarket(item, 1);
02541 forcedgained += item->price;
02542 forcedsold++;
02543 }
02544 }
02545 }
02546 continue;
02547 }
02548 }
02549
02550 if (numitems > 0) {
02551 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("By selling %s you gathered %i credits."),
02552 va(ngettext("%i collected item", "%i collected items", numitems), numitems), gained);
02553 MS_AddNewMessage(_("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
02554 }
02555 if (forcedsold > 0) {
02556 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("Not enough storage space in %s. %s"),
02557 base->name, va(ngettext("%i item was sold for %i credits.", "%i items were sold for %i credits.", forcedsold), forcedsold, forcedgained));
02558 MS_AddNewMessage(_("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
02559 }
02560 CL_UpdateCredits(ccs.credits + gained + forcedgained);
02561
02562
02563 aircraft->itemTypes = 0;
02564
02565
02566 RS_MarkResearchable(qfalse, aircraft->homebase);
02567
02568 B_UpdateStorageCap(aircraft->homebase);
02569 }
02570
02575 void B_DumpAircraftToHomeBase (aircraft_t *aircraft)
02576 {
02577 aliensTmp_t *cargo;
02578
02579
02580 if (aircraft->type != AIRCRAFT_TRANSPORTER)
02581 return;
02582
02583
02584 AL_AddAliens(aircraft);
02585
02586 B_SellOrAddItems(aircraft);
02587
02588
02589 cargo = AL_GetAircraftAlienCargo(aircraft);
02590 memset(cargo, 0, sizeof(*cargo));
02591 memset(aircraft->itemcargo, 0, sizeof(aircraft->itemcargo));
02592 AL_SetAircraftAlienCargoTypes(aircraft, 0);
02593 }
02594
02601 void B_AircraftReturnedToHomeBase (aircraft_t* aircraft)
02602 {
02603
02604 RADAR_InitialiseUFOs(&aircraft->radar);
02605
02606 AII_ReloadAircraftWeapons(aircraft);
02607
02608 B_DumpAircraftToHomeBase(aircraft);
02609 }
02610
02616 qboolean B_BaseHasItem (const base_t *base, const objDef_t *item)
02617 {
02618 assert(base);
02619 assert(item);
02620 return (item->isVirtual || base->storage.numItems[item->idx] > 0);
02621 }
02622
02629 int B_ItemInBase (const objDef_t *item, const base_t *base)
02630 {
02631 const equipDef_t *ed;
02632
02633 if (!item)
02634 return -1;
02635 if (item->isVirtual)
02636 return -1;
02637 if (!base)
02638 return -1;
02639
02640 ed = &base->storage;
02641
02642 if (!ed)
02643 return -1;
02644
02645 return ed->numItems[item->idx];
02646 }
02647
02656 void B_UpdateBaseCapacities (baseCapacities_t cap, base_t *base)
02657 {
02658 int i, capacity = 0, buildingTemplateIDX = -1;
02659 buildingType_t buildingType;
02660 building_t *building;
02661
02662
02663 buildingType = B_GetBuildingTypeByCapacity(cap);
02664
02665 switch (cap) {
02666 case CAP_ALIENS:
02667 case CAP_EMPLOYEES:
02668 case CAP_LABSPACE:
02669 case CAP_WORKSPACE:
02670 case CAP_ITEMS:
02671 case CAP_AIRCRAFT_SMALL:
02672 case CAP_AIRCRAFT_BIG:
02673 case CAP_ANTIMATTER:
02674
02675 base->capacities[cap].max = 0;
02676
02677 for (i = 0; i < ccs.numBuildingTemplates; i++) {
02678 if (ccs.buildingTemplates[i].buildingType == buildingType) {
02679 capacity = ccs.buildingTemplates[i].capacity;
02680 Com_DPrintf(DEBUG_CLIENT, "Building: %s capacity: %i\n", ccs.buildingTemplates[i].id, capacity);
02681 buildingTemplateIDX = i;
02682 break;
02683 }
02684 }
02685
02686 building = NULL;
02687 while ((building = B_GetNextBuildingByType(base, building, buildingType)))
02688 if (building->buildingStatus >= B_STATUS_CONSTRUCTION_FINISHED)
02689 base->capacities[cap].max += capacity;
02690
02691 if (buildingTemplateIDX != -1)
02692 Com_DPrintf(DEBUG_CLIENT, "B_UpdateBaseCapacities: updated capacity of %s: %i\n",
02693 ccs.buildingTemplates[buildingTemplateIDX].id, base->capacities[cap].max);
02694 break;
02695 case MAX_CAP:
02696 Com_DPrintf(DEBUG_CLIENT, "B_UpdateBaseCapacities: going to update ALL capacities.\n");
02697
02698 for (i = 0; i < cap; i++) {
02699 B_UpdateBaseCapacities(i, base);
02700 }
02701 break;
02702 default:
02703 Com_Error(ERR_DROP, "Unknown capacity limit for this base: %i \n", cap);
02704 }
02705 }
02706
02713 void B_SaveBaseSlotsXML (const baseWeapon_t *weapons, const int numWeapons, mxml_node_t *node)
02714 {
02715 int i;
02716
02717 for (i = 0; i < numWeapons; i++) {
02718 mxml_node_t *sub = mxml_AddNode(node, SAVE_BASES_WEAPON);
02719 AII_SaveOneSlotXML(sub, &weapons[i].slot, qtrue);
02720 mxml_AddBoolValue(sub, SAVE_BASES_AUTOFIRE, weapons[i].autofire);
02721 if (weapons[i].target)
02722 mxml_AddInt(sub, SAVE_BASES_TARGET, weapons[i].target->idx);
02723 }
02724 }
02725
02731 qboolean B_SaveStorageXML (mxml_node_t *parent, const equipDef_t equip)
02732 {
02733 int k;
02734 for (k = 0; k < csi.numODs; k++) {
02735 const objDef_t *od = INVSH_GetItemByIDX(k);
02736 if (equip.numItems[k] || equip.numItemsLoose[k]) {
02737 mxml_node_t *node = mxml_AddNode(parent, SAVE_BASES_ITEM);
02738
02739 mxml_AddString(node, SAVE_BASES_ODS_ID, od->id);
02740 mxml_AddIntValue(node, SAVE_BASES_NUM, equip.numItems[k]);
02741 mxml_AddByteValue(node, SAVE_BASES_NUMLOOSE, equip.numItemsLoose[k]);
02742 }
02743 }
02744 return qtrue;
02745 }
02750 qboolean B_SaveXML (mxml_node_t *parent)
02751 {
02752 int i;
02753 mxml_node_t * bases;
02754
02755 bases = mxml_AddNode(parent, SAVE_BASES_BASES);
02756 for (i = 0; i < ccs.numBases; i++) {
02757 int k;
02758 mxml_node_t * act_base, *node;
02759 const base_t *b = B_GetBaseByIDX(i);
02760 building_t *building;
02761
02762 if (!b->founded) {
02763 Com_Printf("B_SaveXML: Base (idx: %i) not founded!\n", b->idx);
02764 return qfalse;
02765 }
02766
02767 Com_RegisterConstList(saveBaseConstants);
02768
02769 act_base = mxml_AddNode(bases, SAVE_BASES_BASE);
02770 mxml_AddString(act_base, SAVE_BASES_NAME, b->name);
02771 mxml_AddPos3(act_base, SAVE_BASES_POS, b->pos);
02772 mxml_AddString(act_base, SAVE_BASES_BASESTATUS, Com_GetConstVariable(SAVE_BASESTATUS_NAMESPACE, b->baseStatus));
02773 mxml_AddFloat(act_base, SAVE_BASES_ALIENINTEREST, b->alienInterest);
02774 if (b->aircraftCurrent)
02775 mxml_AddInt(act_base, SAVE_BASES_CURRENTAIRCRAFTIDX, AIR_GetAircraftIDXInBase(b->aircraftCurrent));
02776
02777
02778 node = mxml_AddNode(act_base, SAVE_BASES_BUILDINGSPACE);
02779 for (k = 0; k < BASE_SIZE; k++) {
02780 int l;
02781 for (l = 0; l < BASE_SIZE; l++) {
02782 mxml_node_t * snode = mxml_AddNode(node, SAVE_BASES_BUILDING);
02783 mxml_AddInt(snode, SAVE_BASES_X, k);
02784 mxml_AddInt(snode, SAVE_BASES_Y, l);
02785 if (b->map[k][l].building)
02786 mxml_AddInt(snode, SAVE_BASES_BUILDINGINDEX, b->map[k][l].building->idx);
02787 mxml_AddBoolValue(snode, SAVE_BASES_BLOCKED, b->map[k][l].blocked);
02788 }
02789 }
02790
02791 node = mxml_AddNode(act_base, SAVE_BASES_BUILDINGS);
02792 building = NULL;
02793 while ((building = B_GetNextBuilding(b, building))) {
02794 mxml_node_t * snode;
02795
02796 if (!building->tpl)
02797 continue;
02798
02799 snode = mxml_AddNode(node, SAVE_BASES_BUILDING);
02800 mxml_AddString(snode, SAVE_BASES_BUILDINGTYPE, building->tpl->id);
02801 mxml_AddInt(snode, SAVE_BASES_BUILDING_PLACE, building->idx);
02802 mxml_AddString(snode, SAVE_BASES_BUILDINGSTATUS, Com_GetConstVariable(SAVE_BUILDINGSTATUS_NAMESPACE, building->buildingStatus));
02803 mxml_AddInt(snode, SAVE_BASES_BUILDINGTIMESTART, building->timeStart);
02804 mxml_AddInt(snode, SAVE_BASES_BUILDINGBUILDTIME, building->buildTime);
02805 mxml_AddFloat(snode, SAVE_BASES_BUILDINGLEVEL, building->level);
02806 mxml_AddPos2(snode, SAVE_BASES_POS, building->pos);
02807 }
02808
02809 node = mxml_AddNode(act_base, SAVE_BASES_BATTERIES);
02810 B_SaveBaseSlotsXML(b->batteries, b->numBatteries, node);
02811 node = mxml_AddNode(act_base, SAVE_BASES_LASERS);
02812 B_SaveBaseSlotsXML(b->lasers, b->numLasers, node);
02813
02814 node = mxml_AddNode(act_base, SAVE_BASES_STORAGE);
02815 B_SaveStorageXML(node, b->storage);
02816
02817 mxml_AddIntValue(act_base, SAVE_BASES_RADARRANGE, b->radar.range);
02818 mxml_AddIntValue(act_base, SAVE_BASES_TRACKINGRANGE, b->radar.trackingRange);
02819
02820 Com_UnregisterConstList(saveBaseConstants);
02821 }
02822 return qtrue;
02823 }
02824
02833 int B_LoadBaseSlotsXML (baseWeapon_t* weapons, int max, mxml_node_t *p)
02834 {
02835 int i;
02836 mxml_node_t *s;
02837 for (i = 0, s = mxml_GetNode(p, SAVE_BASES_WEAPON); s && i < max; i++, s = mxml_GetNextNode(s, p, SAVE_BASES_WEAPON)) {
02838 const int target = mxml_GetInt(s, SAVE_BASES_TARGET, -1);
02839 AII_LoadOneSlotXML(s, &weapons[i].slot, qtrue);
02840 weapons[i].autofire = mxml_GetBool(s, SAVE_BASES_AUTOFIRE, qtrue);
02841 weapons[i].target = (target >= 0) ? UFO_GetByIDX(target) : NULL;
02842 }
02843 return i;
02844 }
02845
02850 static void B_PostLoadInitCapacity (void)
02851 {
02852 int baseIdx;
02853
02854 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
02855 base_t *base = B_GetFoundedBaseByIDX(baseIdx);
02856 if (!base)
02857 continue;
02858
02859 B_ResetAllStatusAndCapacities(base, qtrue);
02860 }
02861 }
02862
02867 void B_PostLoadInit (void)
02868 {
02869 B_PostLoadInitCapacity();
02870 }
02871
02877 qboolean B_LoadStorageXML (mxml_node_t *parent, equipDef_t *equip)
02878 {
02879 mxml_node_t *node;
02880 for (node = mxml_GetNode(parent, SAVE_BASES_ITEM); node; node = mxml_GetNextNode(node, parent, SAVE_BASES_ITEM)) {
02881 const char *s = mxml_GetString(node, SAVE_BASES_ODS_ID);
02882 objDef_t *od = INVSH_GetItemByID(s);
02883
02884 if (!od) {
02885 Com_Printf("B_Load: Could not find item '%s'\n", s);
02886 } else {
02887 equip->numItems[od->idx] = mxml_GetInt(node, SAVE_BASES_NUM, 0);
02888 equip->numItemsLoose[od->idx] = mxml_GetInt(node, SAVE_BASES_NUMLOOSE, 0);
02889 }
02890 }
02891 return qtrue;
02892 }
02893
02898 qboolean B_LoadXML (mxml_node_t *parent)
02899 {
02900 int i;
02901 int j;
02902 int buildingIdx;
02903 mxml_node_t *bases, *base;
02904
02905 bases = mxml_GetNode(parent, "bases");
02906 if (!bases) {
02907 Com_Printf("Error: Base Node wasn't found in savegame\n");
02908 return qfalse;
02909 }
02910
02911 Com_RegisterConstList(saveBaseConstants);
02912 for (base = mxml_GetNode(bases, SAVE_BASES_BASE), i = 0; i < MAX_BASES && base; i++, base = mxml_GetNextNode(base, bases, SAVE_BASES_BASE)) {
02913 mxml_node_t * node, * snode;
02914 int aircraftIdxInBase;
02915 base_t *const b = B_GetBaseByIDX(i);
02916 const char *str = mxml_GetString(base, SAVE_BASES_BASESTATUS);
02917
02918 b->idx = B_GetBaseIDX(b);
02919 b->founded = qtrue;
02920 if (!Com_GetConstIntFromNamespace(SAVE_BASESTATUS_NAMESPACE, str, (int*) &b->baseStatus)) {
02921 Com_Printf("Invaild base status '%s'\n", str);
02922 Com_UnregisterConstList(saveBaseConstants);
02923 return qfalse;
02924 }
02925
02926 Q_strncpyz(b->name, mxml_GetString(base, SAVE_BASES_NAME), sizeof(b->name));
02927 mxml_GetPos3(base, SAVE_BASES_POS, b->pos);
02928 b->alienInterest = mxml_GetFloat(base, SAVE_BASES_ALIENINTEREST, 0.0);
02929
02930 aircraftIdxInBase = mxml_GetInt(base, SAVE_BASES_CURRENTAIRCRAFTIDX, AIRCRAFT_INBASE_INVALID);
02931 if (aircraftIdxInBase != AIRCRAFT_INBASE_INVALID) {
02932
02934
02935 b->aircraftCurrent = NULL;
02936 } else {
02937 b->aircraftCurrent = NULL;
02938 }
02939
02940
02941 node = mxml_GetNode(base, SAVE_BASES_BUILDINGSPACE);
02942 for (snode = mxml_GetNode(node, SAVE_BASES_BUILDING); snode; snode = mxml_GetNextNode(snode, node, SAVE_BASES_BUILDING)) {
02943 const int j = mxml_GetInt(snode, SAVE_BASES_X, 0);
02944 const int l = mxml_GetInt(snode, SAVE_BASES_Y, 0);
02945 buildingIdx = mxml_GetInt(snode, SAVE_BASES_BUILDINGINDEX, -1);
02946
02947 if (buildingIdx != -1)
02948
02949 b->map[j][l].building = &ccs.buildings[i][buildingIdx];
02950 else
02951 b->map[j][l].building = NULL;
02952 b->map[j][l].blocked = mxml_GetBool(snode, SAVE_BASES_BLOCKED, qfalse);
02953 }
02954
02955 node = mxml_GetNode(base, SAVE_BASES_BUILDINGS);
02956 for (j = 0, snode = mxml_GetNode(node, SAVE_BASES_BUILDING); snode; snode = mxml_GetNextNode(snode, node, SAVE_BASES_BUILDING), j++) {
02957 const int buildId = mxml_GetInt(snode, SAVE_BASES_BUILDING_PLACE, MAX_BUILDINGS);
02958 building_t *building;
02959 char buildingType[MAX_VAR];
02960
02961 if (buildId >= MAX_BUILDINGS) {
02962 Com_Printf("building ID is greater than MAX buildings\n");
02963 Com_UnregisterConstList(saveBaseConstants);
02964 return qfalse;
02965 }
02966
02967 Q_strncpyz(buildingType, mxml_GetString(snode, SAVE_BASES_BUILDINGTYPE), sizeof(buildingType));
02968 if (buildingType[0] == '\0') {
02969 Com_Printf("No buildingtype set\n");
02970 Com_UnregisterConstList(saveBaseConstants);
02971 return qfalse;
02972 }
02973
02974 building = B_GetBuildingTemplate(buildingType);
02975 if (!building)
02976 continue;
02977
02978 ccs.buildings[i][buildId] = *building;
02979 building = &ccs.buildings[i][buildId];
02980 building->idx = B_GetBuildingIDX(b, building);
02981 if (building->idx != buildId) {
02982 Com_Printf("building ID doesn't match\n");
02983 Com_UnregisterConstList(saveBaseConstants);
02984 return qfalse;
02985 }
02986 building->base = b;
02987
02988 str = mxml_GetString(snode, SAVE_BASES_BUILDINGSTATUS);
02989 if (!Com_GetConstIntFromNamespace(SAVE_BUILDINGSTATUS_NAMESPACE, str, (int*) &building->buildingStatus)) {
02990 Com_Printf("Invaild building status '%s'\n", str);
02991 Com_UnregisterConstList(saveBaseConstants);
02992 return qfalse;
02993 }
02994
02995 building->timeStart = mxml_GetInt(snode, SAVE_BASES_BUILDINGTIMESTART, 0);
02996 building->buildTime = mxml_GetInt(snode, SAVE_BASES_BUILDINGBUILDTIME, 0);
02997 building->level = mxml_GetFloat(snode, SAVE_BASES_BUILDINGLEVEL, 0);
02998 mxml_GetPos2(snode, SAVE_BASES_POS, building->pos);
02999 }
03000 ccs.numBuildings[i] = j;
03001
03002 BDEF_InitialiseBaseSlots(b);
03003
03004 node = mxml_GetNode(base, SAVE_BASES_BATTERIES);
03005 if (node)
03006 b->numBatteries = B_LoadBaseSlotsXML(b->batteries, MAX_BASE_SLOT, node);
03007
03008 node = mxml_GetNode(base, SAVE_BASES_LASERS);
03009 if (node)
03010 b->numLasers = B_LoadBaseSlotsXML(b->lasers, MAX_BASE_SLOT, node);
03011
03012 node = mxml_GetNode(base, SAVE_BASES_STORAGE);
03013 B_LoadStorageXML(node, &(b->storage));
03014
03015 RADAR_InitialiseUFOs(&b->radar);
03016 RADAR_Initialise(&b->radar, mxml_GetInt(base, SAVE_BASES_RADARRANGE, 0), mxml_GetInt(base, SAVE_BASES_TRACKINGRANGE, 0), B_GetMaxBuildingLevel(b, B_RADAR), qtrue);
03017
03019
03020 memset(&b->bEquipment, 0, sizeof(b->bEquipment));
03021
03022 }
03023 Com_UnregisterConstList(saveBaseConstants);
03024 ccs.numBases = B_GetFoundedBaseCount();
03025 B_UpdateBaseCount();
03026
03027 return qtrue;
03028 }
03029
03035 qboolean B_ItemIsStoredInBaseStorage (const objDef_t *obj)
03036 {
03037
03038 if (obj->isVirtual || !strcmp(obj->id, ANTIMATTER_TECH_ID))
03039 return qfalse;
03040
03041 return qtrue;
03042 }
03043
03054 int B_AddToStorage (base_t* base, const objDef_t *obj, int amount)
03055 {
03056 assert(base);
03057 assert(obj);
03058
03059 if (obj->isVirtual)
03060 return 0;
03061 if (!B_ItemIsStoredInBaseStorage(obj))
03062 return 0;
03063
03064 if (amount > 0) {
03065 if (obj->size > 0) {
03066 const int freeSpace = base->capacities[CAP_ITEMS].max - base->capacities[CAP_ITEMS].cur;
03067
03068 amount = min(amount, freeSpace / obj->size);
03069 base->capacities[CAP_ITEMS].cur += (amount * obj->size);
03070 }
03071 base->storage.numItems[obj->idx] += amount;
03072 } else if (amount < 0) {
03073
03074 amount = max(amount, -base->storage.numItems[obj->idx]);
03075 if (obj->size > 0)
03076 base->capacities[CAP_ITEMS].cur += (amount * obj->size);
03077 base->storage.numItems[obj->idx] += amount;
03078 }
03079
03080 return amount;
03081 }
03082
03093 qboolean B_UpdateStorageAndCapacity (base_t* base, const objDef_t *obj, int amount, qboolean reset, qboolean ignorecap)
03094 {
03095 assert(base);
03096 assert(obj);
03097
03098 if (obj->isVirtual)
03099 return qtrue;
03100
03101 if (reset) {
03102 base->storage.numItems[obj->idx] = 0;
03103 base->storage.numItemsLoose[obj->idx] = 0;
03104 base->capacities[CAP_ITEMS].cur = 0;
03105 } else {
03106 if (!B_ItemIsStoredInBaseStorage(obj)) {
03107 Com_DPrintf(DEBUG_CLIENT, "B_UpdateStorageAndCapacity: Item '%s' is not stored in storage: skip\n", obj->id);
03108 return qfalse;
03109 }
03110
03111 if (!ignorecap && amount > 0) {
03112
03113 if (base->capacities[CAP_ITEMS].max - base->capacities[CAP_ITEMS].cur < (obj->size * amount)) {
03114 Com_DPrintf(DEBUG_CLIENT, "B_UpdateStorageAndCapacity: Not enough storage space (item: %s, amount: %i)\n", obj->id, amount);
03115 return qfalse;
03116 }
03117 }
03118
03119 base->storage.numItems[obj->idx] += amount;
03120 if (obj->size > 0)
03121 base->capacities[CAP_ITEMS].cur += (amount * obj->size);
03122
03123 if (base->capacities[CAP_ITEMS].cur < 0) {
03124 Com_Printf("B_UpdateStorageAndCapacity: current storage capacity is negative (%i): reset to 0\n", base->capacities[CAP_ITEMS].cur);
03125 base->capacities[CAP_ITEMS].cur = 0;
03126 }
03127
03128 if (base->storage.numItems[obj->idx] < 0) {
03129 Com_Printf("B_UpdateStorageAndCapacity: current number of item '%s' is negative: reset to 0\n", obj->id);
03130 base->storage.numItems[obj->idx] = 0;
03131 }
03132 }
03133
03134 return qtrue;
03135 }
03136
03141 qboolean B_ScriptSanityCheck (void)
03142 {
03143 int i, error = 0;
03144 building_t* b;
03145
03146 for (i = 0, b = ccs.buildingTemplates; i < ccs.numBuildingTemplates; i++, b++) {
03147 if (!b->mapPart && b->visible) {
03148 error++;
03149 Com_Printf("...... no mappart for building '%s' given\n", b->id);
03150 }
03151 if (!b->name) {
03152 error++;
03153 Com_Printf("...... no name for building '%s' given\n", b->id);
03154 }
03155 if (!b->image) {
03156 error++;
03157 Com_Printf("...... no image for building '%s' given\n", b->id);
03158 }
03159 if (!b->pedia) {
03160 error++;
03161 Com_Printf("...... no pedia link for building '%s' given\n", b->id);
03162 } else if (!RS_GetTechByID(b->pedia)) {
03163 error++;
03164 Com_Printf("...... could not get pedia entry tech (%s) for building '%s'\n", b->pedia, b->id);
03165 }
03166 }
03167 if (!error)
03168 return qtrue;
03169 else
03170 return qfalse;
03171 }
03172
03178 void B_RemoveItemsExceedingCapacity (base_t *base)
03179 {
03180 int i;
03181 int objIdx[MAX_OBJDEFS];
03182 int num, cnt;
03183
03184 if (base->capacities[CAP_ITEMS].cur <= base->capacities[CAP_ITEMS].max)
03185 return;
03186
03187 for (i = 0, num = 0; i < csi.numODs; i++) {
03188 const objDef_t *obj = INVSH_GetItemByIDX(i);
03189
03190 if (!B_ItemIsStoredInBaseStorage(obj))
03191 continue;
03192
03193
03194 if (!base->storage.numItems[i])
03195 continue;
03196
03197 objIdx[num++] = i;
03198 }
03199
03200 cnt = E_CountHired(base, EMPL_ROBOT);
03201
03202 for (i = 0; i < cnt; i++) {
03203 objIdx[num++] = MAX_OBJDEFS;
03204 }
03205
03206 while (num && base->capacities[CAP_ITEMS].cur > base->capacities[CAP_ITEMS].max) {
03207
03208 const int randNumber = rand() % num;
03209 if (objIdx[randNumber] >= MAX_OBJDEFS) {
03210
03211 employee_t* employee = E_GetHiredRobot(base, 0);
03212
03213 assert(employee);
03214 E_DeleteEmployee(employee, EMPL_ROBOT);
03215 } else {
03216
03217
03218 const int idx = objIdx[randNumber];
03219 objDef_t *od = INVSH_GetItemByIDX(idx);
03220 B_UpdateStorageAndCapacity(base, od, -base->storage.numItems[idx], qfalse, qfalse);
03221 }
03222 REMOVE_ELEM(objIdx, randNumber, num);
03223
03224
03225 if (num <= 0)
03226 break;
03227 }
03228 Com_DPrintf(DEBUG_CLIENT, "B_RemoveItemsExceedingCapacity: Remains %i in storage for a maximum of %i\n",
03229 base->capacities[CAP_ITEMS].cur, base->capacities[CAP_ITEMS].max);
03230 }
03231
03237 void B_UpdateStorageCap (base_t *base)
03238 {
03239 int i;
03240
03241 base->capacities[CAP_ITEMS].cur = 0;
03242
03243 for (i = 0; i < csi.numODs; i++) {
03244 const objDef_t *obj = INVSH_GetItemByIDX(i);
03245
03246 if (!B_ItemIsStoredInBaseStorage(obj))
03247 continue;
03248
03249 base->capacities[CAP_ITEMS].cur += base->storage.numItems[i] * obj->size;
03250 }
03251
03252
03253 base->capacities[CAP_ITEMS].cur += UGV_SIZE * E_CountHired(base, EMPL_ROBOT);
03254 }
03255
03260 int B_AntimatterInBase (const base_t *base)
03261 {
03262 #ifdef DEBUG
03263 objDef_t *od;
03264
03265 od = INVSH_GetItemByID(ANTIMATTER_TECH_ID);
03266 if (od == NULL)
03267 Com_Error(ERR_DROP, "Could not find "ANTIMATTER_TECH_ID" object definition");
03268
03269 assert(base);
03270 assert(base->storage.numItems[od->idx] == base->capacities[CAP_ANTIMATTER].cur);
03271 #endif
03272
03273 return base->capacities[CAP_ANTIMATTER].cur;
03274 }
03275
03284 void B_ManageAntimatter (base_t *base, int amount, qboolean add)
03285 {
03286 objDef_t *od;
03287
03288 assert(base);
03289
03290 if (add && !B_GetBuildingStatus(base, B_ANTIMATTER)) {
03291 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer),
03292 _("%s does not have Antimatter Storage Facility. %i units of antimatter got removed."),
03293 base->name, amount);
03294 MS_AddNewMessage(_("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
03295 return;
03296 }
03297
03298 od = INVSH_GetItemByIDSilent(ANTIMATTER_TECH_ID);
03299 if (od == NULL)
03300 Com_Error(ERR_DROP, "Could not find "ANTIMATTER_TECH_ID" object definition");
03301
03302 if (add) {
03303 const int a = min(amount, base->capacities[CAP_ANTIMATTER].max - base->capacities[CAP_ANTIMATTER].cur);
03304 base->storage.numItems[od->idx] += a;
03305 base->capacities[CAP_ANTIMATTER].cur += a;
03306 } else {
03307 if (amount == 0) {
03308 base->capacities[CAP_ANTIMATTER].cur = 0;
03309 base->storage.numItems[od->idx] = 0;
03310 } else {
03311 const int a = min(amount, base->capacities[CAP_ANTIMATTER].cur);
03312 base->capacities[CAP_ANTIMATTER].cur -= a;
03313 base->storage.numItems[od->idx] -= a;
03314 }
03315 }
03316 }
03317
03322 void B_RemoveAntimatterExceedingCapacity (base_t *base)
03323 {
03324 const int amount = base->capacities[CAP_ANTIMATTER].cur - base->capacities[CAP_ANTIMATTER].max;
03325 if (amount <= 0)
03326 return;
03327
03328 B_ManageAntimatter(base, amount, qfalse);
03329 }