cp_base.c

Go to the documentation of this file.
00001 
00008 /*
00009 Copyright (C) 2002-2010 UFO: Alien Invasion.
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020 See the GNU General Public License for more details.
00021 
00022 You should have received a copy of the GNU General Public License
00023 along with this program; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00025 */
00026 
00027 #include "../client.h" /* cl, cls */
00028 #include "../cl_inventory.h" /* INV_GetEquipmentDefinitionByID */
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             /* don't count any further - the caller doesn't want to know the value */
00143             if (!cnt)
00144                 return qtrue;
00145         }
00146     }
00147 
00148     /* set the cnt pointer if the caller wants to know this value */
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     /* Make sure the dependsBuilding pointer is really a template .. just in case. */
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     /*event handler functions */
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     /* reset the used flag */
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                 /* basemaps with needs are not (like the images in B_DrawBase) two maps - but one
00373                  * this is why we check the used flag and continue if it was set already */
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     /* set maxlevel for base attacks */
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     /* Status of Miscellenious buildings cannot change. */
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         /* if an alien containment is not functional, aliens die... */
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     /* Construction / destruction may have changed the status of other building
00502      * We check that, but only for buildings which needed building */
00503     while ((building = B_GetNextBuilding(base, building))) {
00504         building_t *dependsBuilding = building->dependsBuilding;
00505         if (dependsBuilding && buildingType == dependsBuilding->buildingType) {
00506             /* ccs.buildings[base->idx][i] needs built/removed building */
00507             if (onBuilt && !B_GetBuildingStatus(base, building->buildingType)) {
00508                 /* we can only activate a non operationnal building */
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                 /* we can only deactivate an operationnal building */
00516                 if (B_CheckUpdateBuilding(building, base)) {
00517                     B_UpdateOneBaseBuildingStatusOnDisable(building->buildingType, base);
00518                     test = qtrue;
00519                     returnValue = qtrue;
00520                 }
00521             }
00522         }
00523     }
00524     /* and maybe some updated status have changed status of other building.
00525      * So we check again, until nothing changes. (no condition here for check, it's too complex) */
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                 /* we can only activate a non operationnal building */
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                 /* we can only deactivate an operationnal building */
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     /* reset all values of hasBuilding[] */
00589     for (i = 0; i < MAX_BUILDING_TYPE; i++) {
00590         if (i != B_MISC)
00591             B_SetBuildingStatus(base, i, qfalse);
00592     }
00593     /* activate all buildings that needs to be activated */
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     /* Update all capacities of base */
00608     B_UpdateBaseCapacities(MAX_CAP, base);
00609 
00610     /* calculate capacities.cur for every capacity */
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     /* Check that current capacity is possible -- if we changed values in *.ufo */
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     /* destroy aircraft only if there's not enough hangar (hangar is already destroyed) */
00660     capacity = B_GetCapacityFromBuildingType(buildingType);
00661     if (base->capacities[capacity].cur <= base->capacities[capacity].max)
00662         return;
00663 
00664     numAwayAircraft = 0;
00665     /* destroy one aircraft (must not be sold: may be destroyed by aliens) */
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         /* Only aircraft in hangar will be destroyed by hangar destruction */
00685         if (!AIR_IsAircraftInBase(aircraft)) {
00686             if (AIR_IsAircraftOnGeoscape(aircraft))
00687                 awayAircraft[numAwayAircraft++] = aircraft;
00688             continue;
00689         }
00690 
00691         /* Remove aircraft and aircraft items, but do not fire employees */
00692         AIR_DeleteAircraft(aircraft);
00693         awayAircraft[numAwayAircraft++] = NULL;
00694         return;
00695     }
00696 
00697     if (!numAwayAircraft)
00698         return;
00699     /* All aircraft are away from base, pick up one and change it's homebase */
00700     randomNum = rand() % numAwayAircraft;
00701     if (!CL_DisplayHomebasePopup(awayAircraft[randomNum], qfalse)) {
00702         /* No base can hold this aircraft */
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     /* Don't allow to destroy an entrance. */
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     /* Remove the building from the base map */
00731     if (building->needs) {
00732         /* "Child" building is always right to the "parent" building". */
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     /* Update buildingCurrent */
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         /* Update the link of other buildings */
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             /* there is no more building of this type */
00771             B_SetBuildingStatus(base, buildingType, qfalse);
00772             /* check if this has an impact on other buildings */
00773             B_UpdateStatusBuilding(base, buildingType, qfalse);
00774             /* we may have changed status of several building: update all capacities */
00775             B_UpdateBaseCapacities(MAX_CAP, base);
00776         } else {
00777             /* there is still at least one other building of the same type in base: just update capacity */
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     /* call ondestroy trigger only if building is not under construction
00787      * (we do that after base capacity has been updated) */
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                 /* found a new homebase? */
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     /* do a reverse loop as buildings are going to be destroyed */
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     /* you can't destroy buildings if base is under attack */
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     /* store the pointer to the building you wanna destroy */
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     /* we update the status of the building (we'll call this building building 1) */
01019     test = B_CheckUpdateBuilding(building, base);
01020     if (test)
01021         B_UpdateOneBaseBuildingStatusOnEnable(building->buildingType, base);
01022 
01023     /* now, the status of this building may have changed the status of other building.
01024      * We check that, but only for buildings which needed building 1 */
01025     if (test) {
01026         B_UpdateStatusBuilding(base, building->buildingType, qtrue);
01027         /* we may have changed status of several building: update all capacities */
01028         B_UpdateBaseCapacities(MAX_CAP, base);
01029     } else {
01030         /* no other status than status of building 1 has been modified
01031          * update only status of building 1 */
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     /* new building in base (not a template) */
01056     building_t *buildingNew;
01057 
01058     /* fake a click to basemap */
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     /* now call the onconstruct trigger */
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     /* Initial soldiers and their equipment. */
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     /* Pay for the initial equipment as well as update storage capacity. */
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     /* Finally update credits. */
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         /* find each building in the template */
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     /* we need to set up the mandatory buildings */
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     /* Create random blocked fields in the base.
01167      * The first base never has blocked fields so we skip it. */
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     /* Add aircraft to the first base */
01206     /* buy two first aircraft and hire pilots for them. */
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     /* Find the initial equipment definition for current campaign. */
01221     ed = INV_GetEquipmentDefinitionByID(campaign->equipment);
01222     /* Copy it to base storage. */
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             /* Auto equip interceptors with weapons and ammos */
01234             AIM_AutoEquipAircraft(aircraft);
01235             break;
01236         case AIRCRAFT_TRANSPORTER:
01237             /* Assign and equip soldiers on Dropships */
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     /* count working Command Centers */
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     /* this cvar is used for disabling the base build button on geoscape
01273      * if MAX_BASES was reached */
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     /* setup for first base */
01296     if (ccs.campaignStats.basesBuilt == 0)
01297         B_SetUpFirstBase(base);
01298     else
01299         B_BuildFromTemplate(base, NULL, qtrue);
01300 
01301     /* a new base is not discovered (yet) */
01302     base->alienInterest = newBaseAlienInterest;
01303 
01304     BDEF_InitialiseBaseSlots(base);
01305 
01306     /* Reset Radar range */
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     /* maybe someone call this command before the buildings are parsed?? */
01380     if (!base || !building)
01381         return qfalse;
01382 
01383     /* enough credits to build this? */
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     /* maybe someone call this command before the buildings are parsed?? */
01410     if (!base || !building)
01411         return;
01412 
01413     if (building->buildingStatus < B_STATUS_UNDER_CONSTRUCTION)
01414         /* credits are updated in the construct function */
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     /* template should really be a template */
01443     /*assert(template == template->tpl);*/
01444 
01445     if (0 <= row && row < BASE_SIZE && 0 <= col && col < BASE_SIZE) {
01446         /* new building in base (not a template) */
01447         building_t *buildingNew = &ccs.buildings[base->idx][ccs.numBuildings[base->idx]];
01448 
01449         /* copy building from template list to base-buildings-list */
01450         *buildingNew = *buildingTemplate;
01451 
01452         /* self-link to building-list in base */
01453         buildingNew->idx = B_GetBuildingIDX(base, buildingNew);
01454 
01455         /* Link to the base. */
01456         buildingNew->base = base;
01457 
01458         if (!base->map[row][col].blocked && !base->map[row][col].building) {
01459             /* No building in this place */
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             /* Update number of buildings on the base. */
01481             ccs.numBuildings[base->idx]++;
01482             /* Credits are updated here, too */
01483             B_NewBuilding(base, buildingNew);
01484 
01485             base->map[row][col].building = buildingNew;
01486 
01487             /* where is this building located in our base? */
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     /* maybe someone call this command before the buildings are parsed?? */
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     /* link into menu text array */
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     /* Check if the template really is one. */
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     /* get id list body */
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         /* new entry */
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         /* set standard values */
01701         building->tpl = building;   /* Self-link just in case ... this way we can check if it is a template or not. */
01702         building->idx = -1;         /* No entry in buildings list (yet). */
01703         building->base = NULL;
01704         building->buildingType = MAX_BUILDING_TYPE;
01705         building->dependsBuilding = NULL;
01706         building->visible = qtrue;
01707         building->maxCount = -1;    /* Default: no limit */
01708 
01709         ccs.numBuildingTemplates++;
01710         do {
01711             /* get the name type */
01712             token = Com_EParse(text, errhead, name);
01713             if (!*text)
01714                 break;
01715             if (*token == '}')
01716                 break;
01717 
01718             /* get values */
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                 /* no linking yet */
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                             /* found a definition */
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             /* get the name type */
01773             token = Com_EParse(text, errhead, name);
01774             if (!*text)
01775                 break;
01776             if (*token == '}')
01777                 break;
01778             /* get values */
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     /* we maybe only want to get the working building (e.g. it might the
01803      * case that we don't have a powerplant and thus the searched building
01804      * is not functional) */
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     /* get token */
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     /* create new Template */
01841     baseTemplate = &ccs.baseTemplates[ccs.numBaseTemplates];
01842     baseTemplate->id = Mem_PoolStrDup(name, cp_campaignPool, 0);
01843 
01844     /* clear map for checking duplicate positions and buildingNums for checking moreThanOne constraint */
01845     memset(&map, 0, sizeof(map));
01846     memset(&buildingNums, 0, sizeof(buildingNums));
01847 
01848     ccs.numBaseTemplates++;
01849 
01850     do {
01851         /* get the building */
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         /* check if building type is known */
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         /* get the position */
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         /* check for buildings on same position */
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     /* templates without the must-have buildings can't be used */
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     /* set up a new base */
01972     if (!base) {
01973         /* if player hit the "create base" button while creating base mode is enabled
01974          * that means that player wants to quit this mode */
01975         if (ccs.mapAction == MA_NEWBASE) {
01976             MAP_ResetAction();
01977             return;
01978         }
01979 
01980         if (ccs.numBases < MAX_BASES) {
01981             /* show radar overlay (if not already displayed) */
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         /* no related basic attribute */
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         /* running the loops below is not enough, we need transitive closure */
02062         /* I guess num times is enough --- could anybody prove this? */
02063         /* or perhaps 2 times is enough as long as weapons have 1 skill? */
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                 /* disregard left hand, or dual-wielding guys are too good */
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 /* more use of this skill */
02103                                  || (no1 && no1 == no2)) { /* or earlier on list */
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         /* pack equipment */
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                 /* transfer is only possible when there are at least two bases */
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     /* update menu */
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         /* set buildingStatus[] and capacities.max values */
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         /* If the related technology is NOT researched, don't sell items. */
02521         if (!RS_IsResearched_ptr(tech)) {
02522             /* Items not researched cannot be thrown out even if not enough space in storage. */
02523             B_UpdateStorageAndCapacity(base, item, amount, qfalse, qtrue);
02524             if (amount > 0)
02525                 RS_MarkCollected(tech);
02526             continue;
02527         } else {
02528             /* If the related technology is researched, check the autosell option. */
02529             if (ccs.eMarket.autosell[item->idx]) { /* Sell items if autosell is enabled. */
02530                 BS_AddItemToMarket(item, amount);
02531                 gained += (item->price * amount);
02532                 numitems += amount;
02533             } else {
02534                 int j;
02535                 /* Check whether there is enough space for adding this item.
02536                  * If yes - add. If not - sell. */
02537                 for (j = 0; j < amount; j++) {
02538                     if (!B_UpdateStorageAndCapacity(base, item, 1, qfalse, qfalse)) {
02539                         /* Not enough space, sell item. */
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     /* ship no longer has cargo aboard */
02563     aircraft->itemTypes = 0;
02564 
02565     /* Mark new technologies researchable. */
02566     RS_MarkResearchable(qfalse, aircraft->homebase);
02567     /* Recalculate storage capacity, to fix wrong capacity if a soldier drops something on the ground */
02568     B_UpdateStorageCap(aircraft->homebase);
02569 }
02570 
02575 void B_DumpAircraftToHomeBase (aircraft_t *aircraft)
02576 {
02577     aliensTmp_t *cargo;
02578 
02579     /* Don't call cargo functions if aircraft is not a transporter. */
02580     if (aircraft->type != AIRCRAFT_TRANSPORTER)
02581         return;
02582 
02583     /* Add aliens to Alien Containment. */
02584     AL_AddAliens(aircraft);
02585     /* Sell collected items or add them to storage. */
02586     B_SellOrAddItems(aircraft);
02587 
02588     /* Now empty alien/item cargo just in case. */
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     /* Reset UFO sensored on radar */
02604     RADAR_InitialiseUFOs(&aircraft->radar);
02605     /* Reload weapons */
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     /* Get building type. */
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         /* Reset given capacity in current base. */
02675         base->capacities[cap].max = 0;
02676         /* Get building capacity. */
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         /* Finally update capacity. */
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         /* Loop through all capacities and update them. */
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         /* building space */
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         /* buildings */
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         /* base defences */
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         /* store equipment */
02814         node = mxml_AddNode(act_base, SAVE_BASES_STORAGE);
02815         B_SaveStorageXML(node, b->storage);
02816         /* radar */
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             /* aircraft are not yet loaded! */
02934             /*b->aircraftCurrent = &b->aircraft[aircraftIdxInBase];*/
02935             b->aircraftCurrent = NULL;
02936         } else {
02937             b->aircraftCurrent = NULL;
02938         }
02939 
02940         /* building space*/
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                 /* The buildings are actually parsed _below_. (See PRE_MAXBUI loop) */
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         /* buildings */
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         /* read missile battery slots */
03004         node = mxml_GetNode(base, SAVE_BASES_BATTERIES);
03005         if (node)
03006             b->numBatteries = B_LoadBaseSlotsXML(b->batteries, MAX_BASE_SLOT, node);
03007         /* read laser battery slots */
03008         node = mxml_GetNode(base, SAVE_BASES_LASERS);
03009         if (node)
03010             b->numLasers = B_LoadBaseSlotsXML(b->lasers, MAX_BASE_SLOT, node);
03011         /* read equipment */
03012         node = mxml_GetNode(base, SAVE_BASES_STORAGE);
03013         B_LoadStorageXML(node, &(b->storage));
03014         /* read radar info */
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         /* clear the mess of stray loaded pointers */
03020         memset(&b->bEquipment, 0, sizeof(b->bEquipment));
03021         /* reset capacities */
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     /* antimatter is stored in antimatter storage */
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             /* correct amount and update capacity */
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         /* correct amount */
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             /* Only add items if there is enough room in storage */
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         /* Don't count item that we don't have in base */
03194         if (!base->storage.numItems[i])
03195             continue;
03196 
03197         objIdx[num++] = i;
03198     }
03199 
03200     cnt = E_CountHired(base, EMPL_ROBOT);
03201     /* UGV takes room in storage capacity: we store them with a value MAX_OBJDEFS that can't be used by objIdx */
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         /* Select the item to remove */
03208         const int randNumber = rand() % num;
03209         if (objIdx[randNumber] >= MAX_OBJDEFS) {
03210             /* A UGV is destroyed: get first one */
03211             employee_t* employee = E_GetHiredRobot(base, 0);
03212             /* There should be at least a UGV */
03213             assert(employee);
03214             E_DeleteEmployee(employee, EMPL_ROBOT);
03215         } else {
03216             /* items are destroyed. We guess that all items of a given type are stored in the same location
03217              *  => destroy all items of this type */
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         /* Make sure that we don't have an infinite loop */
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     /* UGV takes room in storage capacity */
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) {  /* Adding. */
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 {    /* Removing. */
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 }

Generated by  doxygen 1.6.2