00001
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "../client.h"
00031 #include "../ui/ui_main.h"
00032 #include "../ui/ui_popup.h"
00033 #include "../../shared/parse.h"
00034 #include "cp_campaign.h"
00035 #include "cp_mapfightequip.h"
00036 #include "cp_map.h"
00037 #include "cp_ufo.h"
00038 #include "cp_alienbase.h"
00039 #include "cp_team.h"
00040 #include "cp_time.h"
00041 #include "cp_missions.h"
00042 #include "save/save_aircraft.h"
00043
00044
00049 aircraft_t* AIR_GetNextFromBase (const base_t *b, aircraft_t *lastAircraft)
00050 {
00051 if (b) {
00052 aircraft_t *aircraft = lastAircraft;
00053 while ((aircraft = (aircraft_t *)LIST_GetNext(b->aircraft, (void*)aircraft)) != NULL) {
00054 if (aircraft->status != AIR_CRASHED)
00055 return aircraft;
00056 }
00057 return aircraft;
00058 }
00059
00060 return NULL;
00061 }
00062
00068 qboolean AIR_BaseHasAircraft (const base_t *base)
00069 {
00070 return base != NULL && AIR_GetNextFromBase(base, NULL) != NULL;
00071 }
00072
00083 static int AIR_UpdateHangarCapForOne (const aircraft_t const *aircraftTemplate, base_t *base)
00084 {
00085 int freeSpace = 0;
00086
00087 assert(aircraftTemplate);
00088 assert(aircraftTemplate == aircraftTemplate->tpl);
00089
00090 if (!base)
00091 return AIRCRAFT_HANGAR_ERROR;
00092
00093 if (!B_GetBuildingStatus(base, B_HANGAR) && !B_GetBuildingStatus(base, B_SMALL_HANGAR)) {
00094 Com_Printf("AIR_UpdateHangarCapForOne: base does not have any hangar - error!\n");
00095 return AIRCRAFT_HANGAR_ERROR;
00096 }
00097
00098 if (aircraftTemplate->size >= AIRCRAFT_LARGE) {
00099 if (!B_GetBuildingStatus(base, B_HANGAR)) {
00100 Com_Printf("AIR_UpdateHangarCapForOne: base does not have big hangar - error!\n");
00101 return AIRCRAFT_HANGAR_ERROR;
00102 }
00103 freeSpace = base->capacities[CAP_AIRCRAFT_BIG].max - base->capacities[CAP_AIRCRAFT_BIG].cur;
00104 if (freeSpace > 0) {
00105 base->capacities[CAP_AIRCRAFT_BIG].cur++;
00106 return AIRCRAFT_HANGAR_BIG;
00107 } else {
00108
00109 Com_Printf("AIR_UpdateHangarCapForOne: no free space!\n");
00110 return AIRCRAFT_HANGAR_ERROR;
00111 }
00112 } else {
00113 if (!B_GetBuildingStatus(base, B_SMALL_HANGAR)) {
00114 Com_Printf("AIR_UpdateHangarCapForOne: base does not have small hangar - error!\n");
00115 return AIRCRAFT_HANGAR_ERROR;
00116 }
00117 freeSpace = base->capacities[CAP_AIRCRAFT_SMALL].max - base->capacities[CAP_AIRCRAFT_SMALL].cur;
00118 if (freeSpace > 0) {
00119 base->capacities[CAP_AIRCRAFT_SMALL].cur++;
00120 return AIRCRAFT_HANGAR_SMALL;
00121 } else {
00122
00123 Com_Printf("AIR_UpdateHangarCapForOne: no free space!\n");
00124 return AIRCRAFT_HANGAR_ERROR;
00125 }
00126 }
00127 }
00128
00135 void AIR_UpdateHangarCapForAll (base_t *base)
00136 {
00137 aircraft_t *aircraft;
00138
00139 if (!base)
00140 return;
00141
00142
00143 base->capacities[CAP_AIRCRAFT_BIG].cur = 0;
00144 base->capacities[CAP_AIRCRAFT_SMALL].cur = 0;
00145
00146 aircraft = NULL;
00147 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
00148 Com_DPrintf(DEBUG_CLIENT, "AIR_UpdateHangarCapForAll: base: %s, aircraft: %s\n", base->name, aircraft->id);
00149 AIR_UpdateHangarCapForOne(aircraft->tpl, base);
00150 }
00151 Com_DPrintf(DEBUG_CLIENT, "AIR_UpdateHangarCapForAll: base capacities.cur: small: %i big: %i\n", base->capacities[CAP_AIRCRAFT_SMALL].cur, base->capacities[CAP_AIRCRAFT_BIG].cur);
00152 }
00153
00154 #ifdef DEBUG
00155
00160 void AIR_ListAircraft_f (void)
00161 {
00162 int k, baseIdx;
00163
00164 if (Cmd_Argc() == 2)
00165 baseIdx = atoi(Cmd_Argv(1));
00166
00167 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
00168 const base_t *base = B_GetFoundedBaseByIDX(baseIdx);
00169 aircraft_t *aircraft;
00170 if (!base)
00171 continue;
00172
00173 Com_Printf("Aircraft in %s: %i\n", base->name, LIST_Count(base->aircraft));
00174
00175 aircraft = NULL;
00176 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
00177 linkedList_t *l;
00178 Com_Printf("Aircraft %s\n", aircraft->name);
00179 Com_Printf("...idx global %i\n", aircraft->idx);
00180 Com_Printf("...homebase: %s\n", aircraft->homebase ? aircraft->homebase->name : "NO HOMEBASE");
00181 for (k = 0; k < aircraft->maxWeapons; k++) {
00182 if (aircraft->weapons[k].item) {
00183 Com_Printf("...weapon slot %i contains %s", k, aircraft->weapons[k].item->id);
00184 if (!aircraft->weapons[k].installationTime)
00185 Com_Printf(" (functional)\n");
00186 else if (aircraft->weapons[k].installationTime > 0)
00187 Com_Printf(" (%i hours before installation is finished)\n",aircraft->weapons[k].installationTime);
00188 else
00189 Com_Printf(" (%i hours before removing is finished)\n",aircraft->weapons[k].installationTime);
00190 if (aircraft->weapons[k].ammo)
00191 if (aircraft->weapons[k].ammoLeft > 1)
00192 Com_Printf("......this weapon is loaded with ammo %s\n", aircraft->weapons[k].ammo->id);
00193 else
00194 Com_Printf("......no more ammo (%s)\n", aircraft->weapons[k].ammo->id);
00195 else
00196 Com_Printf("......this weapon isn't loaded with ammo\n");
00197 }
00198 else
00199 Com_Printf("...weapon slot %i is empty\n", k);
00200 }
00201 if (aircraft->shield.item) {
00202 Com_Printf("...armour slot contains %s", aircraft->shield.item->id);
00203 if (!aircraft->shield.installationTime)
00204 Com_Printf(" (functional)\n");
00205 else if (aircraft->shield.installationTime > 0)
00206 Com_Printf(" (%i hours before installation is finished)\n",aircraft->shield.installationTime);
00207 else
00208 Com_Printf(" (%i hours before removing is finished)\n",aircraft->shield.installationTime);
00209 } else
00210 Com_Printf("...armour slot is empty\n");
00211 for (k = 0; k < aircraft->maxElectronics; k++) {
00212 if (aircraft->electronics[k].item) {
00213 Com_Printf("...electronics slot %i contains %s", k, aircraft->electronics[k].item->id);
00214 if (!aircraft->electronics[k].installationTime)
00215 Com_Printf(" (functional)\n");
00216 else if (aircraft->electronics[k].installationTime > 0)
00217 Com_Printf(" (%i hours before installation is finished)\n",aircraft->electronics[k].installationTime);
00218 else
00219 Com_Printf(" (%i hours before removing is finished)\n",aircraft->electronics[k].installationTime);
00220 }
00221 else
00222 Com_Printf("...electronics slot %i is empty\n", k);
00223 }
00224 if (aircraft->pilot) {
00225 Com_Printf("...pilot: idx: %i name: %s\n", aircraft->pilot->idx, aircraft->pilot->chr.name);
00226 } else {
00227 Com_Printf("...no pilot assigned\n");
00228 }
00229 Com_Printf("...damage: %i\n", aircraft->damage);
00230 Com_Printf("...stats: ");
00231 for (k = 0; k < AIR_STATS_MAX; k++) {
00232 if (k == AIR_STATS_WRANGE)
00233 Com_Printf("%.2f ", aircraft->stats[k] / 1000.0f);
00234 else
00235 Com_Printf("%i ", aircraft->stats[k]);
00236 }
00237 Com_Printf("\n");
00238 Com_Printf("...name %s\n", aircraft->id);
00239 Com_Printf("...type %i\n", aircraft->type);
00240 Com_Printf("...size %i\n", aircraft->maxTeamSize);
00241 Com_Printf("...fuel %i\n", aircraft->fuel);
00242 Com_Printf("...status %s\n", AIR_AircraftStatusToName(aircraft));
00243 Com_Printf("...pos %.0f:%.0f\n", aircraft->pos[0], aircraft->pos[1]);
00244 Com_Printf("...team: (%i/%i)\n", LIST_Count(aircraft->acTeam), aircraft->maxTeamSize);
00245 for (l = aircraft->acTeam; l != NULL; l = l->next) {
00246 const employee_t const *employee = (const employee_t *)l->data;
00247 const character_t *chr = &employee->chr;
00248 Com_Printf("......idx (in global array): %i\n", employee->idx);
00249 if (chr)
00250 Com_Printf(".........name: %s\n", chr->name);
00251 else
00252 Com_Printf(".........ERROR: Could not get character for employee %i\n", employee->idx);
00253 }
00254 }
00255 }
00256 }
00257 #endif
00258
00259 static equipDef_t eTempEq;
00267 static void AII_CollectingAmmo (aircraft_t *aircraft, const invList_t *magazine)
00268 {
00269
00270 eTempEq.numItemsLoose[magazine->item.m->idx] += magazine->item.a;
00271 if (eTempEq.numItemsLoose[magazine->item.m->idx] >= magazine->item.t->ammo) {
00272
00273 eTempEq.numItemsLoose[magazine->item.m->idx] -= magazine->item.t->ammo;
00274 AII_CollectItem(aircraft, magazine->item.m, 1);
00275 }
00276 }
00277
00286 void AII_CollectItem (aircraft_t *aircraft, const objDef_t *item, int amount)
00287 {
00288 int i;
00289 itemsTmp_t *cargo = aircraft->itemcargo;
00290
00291 for (i = 0; i < aircraft->itemTypes; i++) {
00292 if (cargo[i].item == item) {
00293 Com_DPrintf(DEBUG_CLIENT, "AII_CollectItem: collecting %s (%i) amount %i -> %i\n", item->name, item->idx, cargo[i].amount, cargo[i].amount + amount);
00294 cargo[i].amount += amount;
00295 return;
00296 }
00297 }
00298 Com_DPrintf(DEBUG_CLIENT, "AII_CollectItem: adding %s (%i) amount %i\n", item->name, item->idx, amount);
00299 cargo[i].item = item;
00300 cargo[i].amount = amount;
00301 aircraft->itemTypes++;
00302 }
00303
00309 static void AII_CarriedItems (const le_t *soldier)
00310 {
00311 containerIndex_t container;
00312 invList_t *invList;
00313
00314 for (container = 0; container < csi.numIDs; container++) {
00315
00316 if (INVDEF(container)->temp)
00317 continue;
00318 for (invList = CONTAINER(soldier, container); invList; invList = invList->next) {
00319 const objDef_t *item = invList->item.t;
00320 technology_t *tech = RS_GetTechForItem(item);
00321 ccs.eMission.numItems[item->idx]++;
00322 RS_MarkCollected(tech);
00323
00324 if (!item->reload || invList->item.a == 0)
00325 continue;
00326 ccs.eMission.numItemsLoose[invList->item.m->idx] += invList->item.a;
00327 if (ccs.eMission.numItemsLoose[invList->item.m->idx] >= item->ammo) {
00328 ccs.eMission.numItemsLoose[invList->item.m->idx] -= item->ammo;
00329 ccs.eMission.numItems[invList->item.m->idx]++;
00330 }
00331
00332
00333
00334 }
00335 }
00336 }
00337
00349 void AII_CollectingItems (aircraft_t *aircraft, int won)
00350 {
00351 int i, j;
00352 le_t *le = NULL;
00353 itemsTmp_t *cargo;
00354 itemsTmp_t prevItemCargo[MAX_CARGO];
00355 int prevItemTypes;
00356
00357
00358 memcpy(prevItemCargo, aircraft->itemcargo, sizeof(aircraft->itemcargo));
00359 prevItemTypes = aircraft->itemTypes;
00360
00361 memset(aircraft->itemcargo, 0, sizeof(aircraft->itemcargo));
00362
00363
00364 memset(&eTempEq, 0, sizeof(eTempEq));
00365
00366 cargo = aircraft->itemcargo;
00367 aircraft->itemTypes = 0;
00368
00369 while ((le = LE_GetNextInUse(le))) {
00370
00371
00372
00373 if (LE_IsItem(le)) {
00374 if (won) {
00375 invList_t *item;
00376 for (item = FLOOR(le); item; item = item->next) {
00377 AII_CollectItem(aircraft, item->item.t, 1);
00378 if (item->item.t->reload && item->item.a > 0)
00379 AII_CollectingAmmo(aircraft, item);
00380 }
00381 }
00382 } else if (LE_IsActor(le)) {
00383
00384
00385
00386 if (won && LE_IsDead(le)) {
00387 invList_t *item = ARMOUR(le);
00388 if (item)
00389 AII_CollectItem(aircraft, item->item.t, 1);
00390 } else if (le->team == cls.team && !LE_IsDead(le)) {
00391
00392 AII_CarriedItems(le);
00393 }
00394 }
00395 }
00396
00397 ccs.missionResults.itemTypes = aircraft->itemTypes;
00398 for (i = 0; i < aircraft->itemTypes; i++)
00399 ccs.missionResults.itemAmount += cargo[i].amount;
00400
00401 #ifdef DEBUG
00402
00403 for (i = 0; i < aircraft->itemTypes; i++) {
00404 if (cargo[i].amount > 0)
00405 Com_DPrintf(DEBUG_CLIENT, "Collected items: idx: %i name: %s amount: %i\n", cargo[i].item->idx, cargo[i].item->name, cargo[i].amount);
00406 }
00407 #endif
00408
00409
00410 for (i = 0; i < prevItemTypes; i++) {
00411 for (j = 0; j < aircraft->itemTypes; j++) {
00412 if (cargo[j].item == prevItemCargo[i].item) {
00413 cargo[j].amount += prevItemCargo[i].amount;
00414 break;
00415 }
00416 }
00417 if (j == aircraft->itemTypes) {
00418 cargo[j] = prevItemCargo[i];
00419 aircraft->itemTypes++;
00420 }
00421 }
00422 }
00423
00429 const char *AIR_AircraftStatusToName (const aircraft_t * aircraft)
00430 {
00431 assert(aircraft);
00432 assert(aircraft->homebase);
00433
00434
00435 if (aircraft->homebase->baseStatus == BASE_UNDER_ATTACK &&
00436 AIR_IsAircraftInBase(aircraft))
00437 return _("ON RED ALERT");
00438
00439 switch (aircraft->status) {
00440 case AIR_NONE:
00441 return _("Nothing - should not be displayed");
00442 case AIR_HOME:
00443 return _("at home base");
00444 case AIR_REFUEL:
00445 return _("refuelling");
00446 case AIR_IDLE:
00447 return _("idle");
00448 case AIR_TRANSIT:
00449 return _("in transit");
00450 case AIR_MISSION:
00451 return _("enroute to mission");
00452 case AIR_UFO:
00453 return _("pursuing a UFO");
00454 case AIR_DROP:
00455 return _("ready to drop soldiers");
00456 case AIR_INTERCEPT:
00457 return _("intercepting a UFO");
00458 case AIR_TRANSFER:
00459 return _("enroute to new home base");
00460 case AIR_RETURNING:
00461 return _("returning to base");
00462 case AIR_CRASHED:
00463 Com_Error(ERR_DROP, "AIR_CRASHED should not be visible anywhere");
00464 }
00465 return NULL;
00466 }
00467
00474 qboolean AIR_IsAircraftInBase (const aircraft_t * aircraft)
00475 {
00476 if (aircraft->status == AIR_HOME || aircraft->status == AIR_REFUEL)
00477 return qtrue;
00478 return qfalse;
00479 }
00480
00488 qboolean AIR_IsAircraftOnGeoscape (const aircraft_t * aircraft)
00489 {
00490 switch (aircraft->status) {
00491 case AIR_IDLE:
00492 case AIR_TRANSIT:
00493 case AIR_MISSION:
00494 case AIR_UFO:
00495 case AIR_DROP:
00496 case AIR_INTERCEPT:
00497 case AIR_RETURNING:
00498 return qtrue;
00499 case AIR_NONE:
00500 case AIR_REFUEL:
00501 case AIR_HOME:
00502 case AIR_TRANSFER:
00503 case AIR_CRASHED:
00504 return qfalse;
00505 }
00506
00507 Com_Error(ERR_FATAL, "Unknown aircraft status %i", aircraft->status);
00508 }
00509
00515 int AIR_CountTypeInBase (const base_t *base, aircraftType_t aircraftType)
00516 {
00517 int count = 0;
00518 aircraft_t *aircraft;
00519
00520 aircraft = NULL;
00521 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL)
00522 if (aircraft->type == aircraftType)
00523 count++;
00524
00525 return count;
00526 }
00527
00531 const char *AIR_GetAircraftString (aircraftType_t aircraftType)
00532 {
00533 switch (aircraftType) {
00534 case AIRCRAFT_INTERCEPTOR:
00535 return _("Interceptor");
00536 case AIRCRAFT_TRANSPORTER:
00537 return _("Transporter");
00538 case AIRCRAFT_UFO:
00539 return _("UFO");
00540 }
00541 return "";
00542 }
00543
00549 int CL_AircraftMenuStatsValues (const int value, const int stat)
00550 {
00551 switch (stat) {
00552 case AIR_STATS_SPEED:
00553 case AIR_STATS_MAXSPEED:
00554
00555 return 10 * (int) (111.2 * value / 10.0f);
00556 case AIR_STATS_FUELSIZE:
00557 return value / 1000;
00558 default:
00559 return value;
00560 }
00561 }
00562
00568 int AIR_GetOperationRange (const aircraft_t *aircraft)
00569 {
00570 const int range = aircraft->stats[AIR_STATS_SPEED] * aircraft->stats[AIR_STATS_FUELSIZE];
00571
00572 return 100 * (int) (KILOMETER_PER_DEGREE * range / (2.0f * (float)SECONDS_PER_HOUR * 100.0f));
00573 }
00574
00580 int AIR_GetRemainingRange (const aircraft_t *aircraft)
00581 {
00582 return aircraft->stats[AIR_STATS_SPEED] * aircraft->fuel;
00583 }
00584
00592 qboolean AIR_AircraftHasEnoughFuel (const aircraft_t *aircraft, const vec2_t destination)
00593 {
00594 base_t *base;
00595 float distance;
00596
00597 assert(aircraft);
00598 base = (base_t *) aircraft->homebase;
00599 assert(base);
00600
00601
00602 distance = GetDistanceOnGlobe(aircraft->pos, destination);
00603
00604
00605 distance += GetDistanceOnGlobe(destination, base->pos);
00606
00607
00608 return (distance <= AIR_GetRemainingRange(aircraft) / (float)SECONDS_PER_HOUR);
00609 }
00610
00618 qboolean AIR_AircraftHasEnoughFuelOneWay (const aircraft_t const *aircraft, const vec2_t destination)
00619 {
00620 float distance;
00621
00622 assert(aircraft);
00623
00624
00625 distance = GetDistanceOnGlobe(aircraft->pos, destination);
00626
00627
00628 return (distance <= AIR_GetRemainingRange(aircraft) / (float)SECONDS_PER_HOUR);
00629 }
00630
00636 void AIR_AircraftReturnToBase (aircraft_t *aircraft)
00637 {
00638 if (aircraft && AIR_IsAircraftOnGeoscape(aircraft)) {
00639 const base_t *base = aircraft->homebase;
00640 assert(base);
00641 Com_DPrintf(DEBUG_CLIENT, "return '%s' (%i) to base ('%s').\n", aircraft->id, aircraft->idx, base->name);
00642 MAP_MapCalcLine(aircraft->pos, base->pos, &aircraft->route);
00643 aircraft->status = AIR_RETURNING;
00644 aircraft->time = 0;
00645 aircraft->point = 0;
00646 aircraft->mission = NULL;
00647 }
00648 }
00649
00655 int AIR_GetAircraftIDXInBase (const aircraft_t* aircraft)
00656 {
00657 int i;
00658 base_t *base;
00659 aircraft_t *aircraftInBase;
00660
00661 if (!aircraft || !aircraft->homebase)
00662 return AIRCRAFT_INBASE_INVALID;
00663
00664 base = aircraft->homebase;
00665
00666 i = 0;
00667 aircraftInBase = NULL;
00668 while ((aircraftInBase = AIR_GetNextFromBase(base, aircraftInBase)) != NULL) {
00669 if (aircraftInBase == aircraft)
00670 return i;
00671 i++;
00672 }
00673
00674
00675 return AIRCRAFT_INBASE_INVALID;
00676 }
00677
00683 aircraft_t *AIR_GetAircraftFromBaseByIDXSafe (base_t* base, int index)
00684 {
00685 aircraft_t *aircraft;
00686 int i;
00687
00688 i = 0;
00689 aircraft = NULL;
00690 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
00691 if (index == i)
00692 return aircraft;
00693 i++;
00694 }
00695
00696 return NULL;
00697 }
00698
00705 aircraft_t *AIR_GetAircraftSilent (const char *name)
00706 {
00707 int i;
00708
00709 if (!name)
00710 return NULL;
00711 for (i = 0; i < ccs.numAircraftTemplates; i++) {
00712 aircraft_t *aircraftTemplate = &ccs.aircraftTemplates[i];
00713 if (!strcmp(aircraftTemplate->id, name))
00714 return aircraftTemplate;
00715 }
00716 return NULL;
00717 }
00718
00724 aircraft_t *AIR_GetAircraft (const char *name)
00725 {
00726 aircraft_t *aircraft = AIR_GetAircraftSilent(name);
00727 if (name == NULL || name[0] == '\0')
00728 Com_Error(ERR_DROP, "AIR_GetAircraft called with NULL name!");
00729 else if (aircraft == NULL)
00730 Com_Error(ERR_DROP, "Aircraft '%s' not found", name);
00731
00732 return aircraft;
00733 }
00734
00739 static void AII_SetAircraftInSlots (aircraft_t *aircraft)
00740 {
00741 int i;
00742
00743
00744 for (i = 0; i < MAX_AIRCRAFTSLOT; i++) {
00745 aircraft->weapons[i].aircraft = aircraft;
00746 aircraft->electronics[i].aircraft = aircraft;
00747 }
00748 aircraft->shield.aircraft = aircraft;
00749 }
00750
00759 aircraft_t *AIR_Add (base_t *base, const aircraft_t *aircraftTemplate)
00760 {
00761 aircraft_t *aircraft = (aircraft_t *)LIST_Add(&base->aircraft, (const byte *)aircraftTemplate, sizeof(*aircraftTemplate))->data;
00762 aircraft->homebase = base;
00763 ccs.numAircraft++;
00764 return aircraft;
00765 }
00766
00774 qboolean AIR_Delete (base_t *base, const aircraft_t *aircraft)
00775 {
00776 ccs.numAircraft--;
00777 return LIST_Remove(&base->aircraft, (const void *)aircraft);
00778 }
00779
00786 aircraft_t* AIR_NewAircraft (base_t *base, const char *name)
00787 {
00788 const aircraft_t *aircraftTemplate = AIR_GetAircraft(name);
00789
00790
00791
00792
00793 aircraft_t *aircraft = AIR_Add(base, aircraftTemplate);
00794 aircraft->idx = ccs.campaignStats.aircraftHad++;
00795 aircraft->homebase = base;
00796
00797 AII_UpdateAircraftStats(aircraft);
00798
00799 AII_SetAircraftInSlots(aircraft);
00800
00801 aircraft->fuel = aircraft->stats[AIR_STATS_FUELSIZE];
00802
00803 aircraft->damage = aircraft->stats[AIR_STATS_DAMAGE];
00804
00805 Q_strncpyz(aircraft->name, _(aircraft->defaultName), lengthof(aircraft->name));
00806
00807
00808 VectorSet(aircraft->direction, 1, 0, 0);
00809
00810 AIR_ResetAircraftTeam(aircraft);
00811
00812 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new %s is ready in %s"), _(aircraft->tpl->name), base->name);
00813 MS_AddNewMessage(_("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
00814 Com_DPrintf(DEBUG_CLIENT, "Setting aircraft to pos: %.0f:%.0f\n", base->pos[0], base->pos[1]);
00815 Vector2Copy(base->pos, aircraft->pos);
00816 RADAR_Initialise(&aircraft->radar, RADAR_AIRCRAFTRANGE, RADAR_AIRCRAFTTRACKINGRANGE, 1.0f, qfalse);
00817
00818
00819 Com_DPrintf(DEBUG_CLIENT, "idx_sample: %i name: %s weight: %i\n", aircraft->tpl->idx, aircraft->id, aircraft->size);
00820 Com_DPrintf(DEBUG_CLIENT, "Adding new aircraft %s with IDX %i for %s\n", aircraft->id, aircraft->idx, base->name);
00821 if (!base->aircraftCurrent)
00822 base->aircraftCurrent = aircraft;
00823 aircraft->hangar = AIR_UpdateHangarCapForOne(aircraft->tpl, base);
00824 if (aircraft->hangar == AIRCRAFT_HANGAR_ERROR)
00825 Com_Printf("AIR_NewAircraft: ERROR, new aircraft but no free space in hangars!\n");
00826
00827 Cmd_ExecuteString("base_init");
00828 return aircraft;
00829 }
00830
00831 int AIR_GetCapacityByAircraftWeight (const aircraft_t *aircraft)
00832 {
00833 switch (aircraft->size) {
00834 case AIRCRAFT_SMALL:
00835 return CAP_AIRCRAFT_SMALL;
00836 case AIRCRAFT_LARGE:
00837 return CAP_AIRCRAFT_BIG;
00838 }
00839 Com_Error(ERR_DROP, "AIR_GetCapacityByAircraftWeight: Unknown weight of aircraft '%i'\n", aircraft->size);
00840 }
00841
00846 static int AIR_GetStorageRoom (const aircraft_t *aircraft)
00847 {
00848 int size = 0;
00849 linkedList_t* l;
00850
00851 for (l = aircraft->acTeam; l != NULL; l = l->next) {
00852 const employee_t const *employee = (const employee_t *)l->data;
00853 containerIndex_t container;
00854 for (container = 0; container < csi.numIDs; container++) {
00855 invList_t *ic;
00856 #if 0
00857
00858 if (INVDEF(container)->temp)
00859 continue;
00860 #endif
00861 for (ic = CONTAINER(&employee->chr, container); ic; ic = ic->next) {
00862 const objDef_t *obj = ic->item.t;
00863 size += obj->size;
00864
00865 obj = ic->item.m;
00866 if (obj)
00867 size += obj->size;
00868 }
00869 }
00870 }
00871
00872 return size;
00873 }
00874
00875 const char *AIR_CheckMoveIntoNewHomebase (const aircraft_t *aircraft, const base_t* base, const int capacity)
00876 {
00877 if (!B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(capacity)))
00878 return _("No operational hangars at that base.");
00879
00880
00881 if (base->capacities[capacity].cur >= base->capacities[capacity].max)
00882 return _("No free hangars at that base.");
00883
00884 if (aircraft->maxTeamSize + ((aircraft->pilot) ? 1 : 0) + base->capacities[CAP_EMPLOYEES].cur > base->capacities[CAP_EMPLOYEES].max)
00885 return _("Insufficient free crew quarter space at that base.");
00886
00887 if (aircraft->maxTeamSize && base->capacities[CAP_ITEMS].cur + AIR_GetStorageRoom(aircraft) > base->capacities[CAP_ITEMS].max)
00888 return _("Insufficient storage space at that base.");
00889
00890
00891 if (!AIR_AircraftHasEnoughFuelOneWay(aircraft, base->pos))
00892 return _("That base is beyond this aircraft's range.");
00893
00894 return NULL;
00895 }
00896
00903 static void AIR_TransferItemsCarriedByCharacterToBase (character_t *chr, base_t *sourceBase, base_t* destBase)
00904 {
00905 const invList_t *ic;
00906 containerIndex_t container;
00907
00908 for (container = 0; container < csi.numIDs; container++) {
00909 #if 0
00910
00911 if (INVDEF(container)->temp)
00912 continue;
00913 #endif
00914 for (ic = CONTAINER(chr, container); ic; ic = ic->next) {
00915 const objDef_t *obj = ic->item.t;
00916 B_UpdateStorageAndCapacity(sourceBase, obj, -1, qfalse, qfalse);
00917 B_UpdateStorageAndCapacity(destBase, obj, 1, qfalse, qfalse);
00918
00919 obj = ic->item.m;
00920 if (obj) {
00921 B_UpdateStorageAndCapacity(sourceBase, obj, -1, qfalse, qfalse);
00922 B_UpdateStorageAndCapacity(destBase, obj, 1, qfalse, qfalse);
00923 }
00924 }
00925 }
00926 }
00927
00934 qboolean AIR_MoveAircraftIntoNewHomebase (aircraft_t *aircraft, base_t *base)
00935 {
00936 base_t *oldBase;
00937 baseCapacities_t capacity;
00938 aircraft_t *aircraftDest;
00939 linkedList_t* l;
00940 int i;
00941
00942 assert(aircraft);
00943 assert(base);
00944 assert(base != aircraft->homebase);
00945
00946 Com_DPrintf(DEBUG_CLIENT, "AIR_MoveAircraftIntoNewHomebase: Change homebase of '%s' to '%s'\n", aircraft->id, base->name);
00947
00948
00949 if (aircraft->status == AIR_TRANSFER) {
00950
00951 VectorCopy(base->pos, aircraft->pos);
00952 aircraft->status = AIR_HOME;
00953 }
00954
00955 capacity = AIR_GetCapacityByAircraftWeight(aircraft);
00956 if (AIR_CheckMoveIntoNewHomebase(aircraft, base, capacity))
00957 return qfalse;
00958
00959 oldBase = aircraft->homebase;
00960 assert(oldBase);
00961
00962
00963 E_MoveIntoNewBase(aircraft->pilot, base);
00964
00965 for (l = aircraft->acTeam; l != NULL; l = l->next) {
00966 employee_t *employee = (employee_t *)l->data;
00967 E_MoveIntoNewBase(employee, base);
00968
00969 AIR_TransferItemsCarriedByCharacterToBase(&employee->chr, oldBase, base);
00970 }
00971
00972
00973 aircraftDest = AIR_Add(base, aircraft);
00974 base->capacities[capacity].cur++;
00975
00976
00977 for (i = 0; i < aircraftDest->maxWeapons; i++) {
00978 aircraftDest->weapons[i].aircraft = aircraftDest;
00979 }
00980 for (i = 0; i < aircraftDest->maxElectronics; i++) {
00981 aircraftDest->electronics[i].aircraft = aircraftDest;
00982 }
00983 aircraftDest->shield.aircraft = aircraftDest;
00984
00985
00986 AIR_Delete(oldBase, aircraft);
00987 oldBase->capacities[capacity].cur--;
00988
00989 if (oldBase->aircraftCurrent == aircraft)
00990 oldBase->aircraftCurrent = AIR_GetNextFromBase(oldBase, NULL);
00991
00992 if (!base->aircraftCurrent)
00993 base->aircraftCurrent = aircraftDest;
00994
00995
00996
00997 MAP_SelectAircraft(aircraftDest);
00998
00999 if (aircraftDest->status == AIR_RETURNING) {
01000
01001 AIR_AircraftReturnToBase(aircraftDest);
01002 }
01003
01004 return qtrue;
01005 }
01006
01020 void AIR_DeleteAircraft (aircraft_t *aircraft)
01021 {
01022 int i;
01023 base_t *base;
01024
01025 const qboolean aircraftIsOnGeoscape = AIR_IsAircraftOnGeoscape(aircraft);
01026
01027 assert(aircraft);
01028 base = aircraft->homebase;
01029 assert(base);
01030
01031 MAP_NotifyAircraftRemoved(aircraft);
01032 TR_NotifyAircraftRemoved(aircraft);
01033
01034
01035 AIR_RemoveEmployees(aircraft);
01036
01037
01038
01039
01040 for (i = 0; i < MAX_AIRCRAFTSLOT; i++) {
01041 AII_RemoveItemFromSlot(NULL, aircraft->weapons, qfalse);
01042 AII_RemoveItemFromSlot(NULL, aircraft->electronics, qfalse);
01043 }
01044 AII_RemoveItemFromSlot(NULL, &aircraft->shield, qfalse);
01045
01046 if (base->aircraftCurrent == aircraft)
01047 base->aircraftCurrent = NULL;
01048
01049 AIR_Delete(base, aircraft);
01050
01051 if (!AIR_BaseHasAircraft(base)) {
01052 Cvar_SetValue("mn_equipsoldierstate", 0);
01053 Cvar_Set("mn_aircraftstatus", "");
01054 Cvar_Set("mn_aircraftinbase", "0");
01055 Cvar_Set("mn_aircraftname", "");
01056 Cvar_Set("mn_aircraft_model", "");
01057 } else if (base->aircraftCurrent == NULL) {
01058 base->aircraftCurrent = AIR_GetNextFromBase(base, NULL);
01059 }
01060
01061
01062 Cmd_ExecuteString("base_init");
01063
01064
01065 AIR_UpdateHangarCapForAll(base);
01066
01067
01068 if (aircraftIsOnGeoscape)
01069 RADAR_UpdateWholeRadarOverlay();
01070 }
01071
01078 void AIR_DestroyAircraft (aircraft_t *aircraft)
01079 {
01080 linkedList_t* l;
01081
01082 assert(aircraft);
01083
01084 for (l = aircraft->acTeam; l != NULL;) {
01085 employee_t *employee = (employee_t *)l->data;
01086 l = l->next;
01087 E_RemoveInventoryFromStorage(employee);
01088 E_DeleteEmployee(employee, employee->type);
01089 }
01090
01091
01092 if (aircraft->pilot && E_DeleteEmployee(aircraft->pilot, aircraft->pilot->type)) {
01093 aircraft->pilot = NULL;
01094 } else {
01095
01096 Com_Error(ERR_DROP, "AIR_DestroyAircraft: aircraft id %s had no pilot\n", aircraft->id);
01097 }
01098
01099 AIR_DeleteAircraft(aircraft);
01100 }
01101
01108 qboolean AIR_AircraftMakeMove (int dt, aircraft_t* aircraft)
01109 {
01110 float dist;
01111
01112
01113 aircraft->time += dt;
01114 aircraft->fuel -= dt;
01115
01116 dist = (float) aircraft->stats[AIR_STATS_SPEED] * aircraft->time / (float)SECONDS_PER_HOUR;
01117
01118
01119 if (dist >= aircraft->route.distance * (aircraft->route.numPoints - 1)) {
01120 return qtrue;
01121 } else {
01122
01123 float frac = dist / aircraft->route.distance;
01124 const int p = (int) frac;
01125 frac -= p;
01126 aircraft->point = p;
01127 aircraft->pos[0] = (1 - frac) * aircraft->route.point[p][0] + frac * aircraft->route.point[p + 1][0];
01128 aircraft->pos[1] = (1 - frac) * aircraft->route.point[p][1] + frac * aircraft->route.point[p + 1][1];
01129
01130 MAP_CheckPositionBoundaries(aircraft->pos);
01131 }
01132
01133 aircraft->hasMoved = qtrue;
01134 aircraft->numInterpolationPoints = 0;
01135
01136 dist = (float) aircraft->stats[AIR_STATS_SPEED] * (aircraft->time + dt) / (float)SECONDS_PER_HOUR;
01137
01138
01139
01140 if (dist >= aircraft->route.distance * (aircraft->route.numPoints - 1)) {
01141 VectorSet(aircraft->projectedPos, 0.0f, 0.0f, 0.0f);
01142 } else {
01143
01144 float frac = dist / aircraft->route.distance;
01145 const int p = (int) frac;
01146 frac -= p;
01147 aircraft->projectedPos[0] = (1 - frac) * aircraft->route.point[p][0] + frac * aircraft->route.point[p + 1][0];
01148 aircraft->projectedPos[1] = (1 - frac) * aircraft->route.point[p][1] + frac * aircraft->route.point[p + 1][1];
01149
01150 MAP_CheckPositionBoundaries(aircraft->projectedPos);
01151 }
01152
01153 return qfalse;
01154 }
01155
01156 static void AIR_Move (aircraft_t* aircraft, int deltaTime)
01157 {
01158
01159 if (AIR_AircraftMakeMove(deltaTime, aircraft)) {
01160
01161 const float *end = aircraft->route.point[aircraft->route.numPoints - 1];
01162 Vector2Copy(end, aircraft->pos);
01163 MAP_CheckPositionBoundaries(aircraft->pos);
01164
01165 switch (aircraft->status) {
01166 case AIR_MISSION:
01167
01168 assert(aircraft->mission);
01169 aircraft->mission->active = qtrue;
01170 aircraft->status = AIR_DROP;
01171 ccs.missionAircraft = aircraft;
01172 MAP_SelectMission(ccs.missionAircraft->mission);
01173 ccs.interceptAircraft = ccs.missionAircraft;
01174 Com_DPrintf(DEBUG_CLIENT, "ccs.interceptAircraft: %i\n", ccs.interceptAircraft->idx);
01175 CL_GameTimeStop();
01176 UI_PushWindow("popup_intercept_ready", NULL);
01177 break;
01178 case AIR_RETURNING:
01179
01180 aircraft->status = AIR_REFUEL;
01181 B_AircraftReturnedToHomeBase(aircraft);
01182 break;
01183 case AIR_TRANSFER:
01184 case AIR_UFO:
01185 break;
01186 default:
01187 aircraft->status = AIR_IDLE;
01188 break;
01189 }
01190 }
01191 }
01192
01193 static void AIR_Refuel (aircraft_t *aircraft, int deltaTime)
01194 {
01195
01196 int fillup;
01197
01198 if (aircraft->fuel < 0)
01199 aircraft->fuel = 0;
01200
01201 fillup = min(deltaTime * AIRCRAFT_REFUEL_FACTOR, aircraft->stats[AIR_STATS_FUELSIZE] - aircraft->fuel);
01202
01203 assert(aircraft->homebase);
01204 if (aircraft->stats[AIR_STATS_ANTIMATTER] > 0 && fillup > 0) {
01205
01206 const int amAvailable = B_ItemInBase(INVSH_GetItemByID(ANTIMATTER_TECH_ID), aircraft->homebase);
01207
01208 const int amCurrentLevel = aircraft->stats[AIR_STATS_ANTIMATTER] * (aircraft->fuel / (float) aircraft->stats[AIR_STATS_FUELSIZE]);
01209
01210 const int amNextLevel = aircraft->stats[AIR_STATS_ANTIMATTER] * ((aircraft->fuel + fillup) / (float) aircraft->stats[AIR_STATS_FUELSIZE]);
01211
01212 int amLoad = amNextLevel - amCurrentLevel;
01213
01214 if (amLoad > amAvailable) {
01215
01216 fillup = aircraft->stats[AIR_STATS_FUELSIZE] * ((amCurrentLevel + amAvailable) / (float) aircraft->stats[AIR_STATS_ANTIMATTER]) - aircraft->fuel;
01217 amLoad = amAvailable;
01218
01219 if (!aircraft->notifySent[AIR_CANNOT_REFUEL]) {
01220 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer),
01221 _("Craft %s couldn't be completely refueled at %s. Not enough antimatter."), aircraft->name, aircraft->homebase->name);
01222 MSO_CheckAddNewMessage(NT_AIRCRAFT_CANNOTREFUEL, _("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
01223 aircraft->notifySent[AIR_CANNOT_REFUEL] = qtrue;
01224 }
01225 }
01226
01227 if (amLoad > 0)
01228 B_ManageAntimatter(aircraft->homebase, amLoad, qfalse);
01229 }
01230
01231 aircraft->fuel += fillup;
01232
01233 if (aircraft->fuel >= aircraft->stats[AIR_STATS_FUELSIZE]) {
01234 aircraft->fuel = aircraft->stats[AIR_STATS_FUELSIZE];
01235 aircraft->status = AIR_HOME;
01236 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer),
01237 _("Craft %s has refueled at %s."), aircraft->name, aircraft->homebase->name);
01238 MSO_CheckAddNewMessage(NT_AIRCRAFT_REFUELED, _("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
01239 aircraft->notifySent[AIR_CANNOT_REFUEL] = qfalse;
01240 }
01241
01242 }
01243
01250 void CL_CampaignRunAircraft (int dt, qboolean updateRadarOverlay)
01251 {
01252
01253
01254
01255 static qboolean radarOverlayReset = qfalse;
01256
01257 assert(dt >= 0);
01258
01259 if (dt > 0) {
01260 int j;
01261 for (j = 0; j < MAX_BASES; j++) {
01262 base_t *base = B_GetBaseByIDX(j);
01263 aircraft_t *aircraft;
01264
01265
01266 aircraft = NULL;
01267 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
01268 int k;
01269 assert(aircraft->homebase);
01270 if (aircraft->status == AIR_IDLE) {
01271
01272 aircraft->fuel -= dt;
01273 } else if (AIR_IsAircraftOnGeoscape(aircraft)) {
01274 AIR_Move(aircraft, dt);
01275
01276 radarOverlayReset = qtrue;
01277 } else if (aircraft->status == AIR_REFUEL) {
01278 AIR_Refuel(aircraft, dt);
01279 }
01280
01281
01282 if (aircraft->status != AIR_RETURNING && AIR_IsAircraftOnGeoscape(aircraft) &&
01283 !AIR_AircraftHasEnoughFuel(aircraft, aircraft->pos)) {
01285 MS_AddNewMessage(_("Notice"), va(_("Craft %s is low on fuel and must return to base."), aircraft->name), qfalse, MSG_STANDARD, NULL);
01286 AIR_AircraftReturnToBase(aircraft);
01287 }
01288
01289
01290 if (aircraft->status == AIR_UFO) {
01291
01292 AIRFIGHT_ExecuteActions(aircraft, aircraft->aircraftTarget);
01293 }
01294
01295 for (k = 0; k < aircraft->maxWeapons; k++) {
01296
01297 if (AIR_IsAircraftOnGeoscape(aircraft) && (aircraft->weapons[k].delayNextShot > 0))
01298 aircraft->weapons[k].delayNextShot -= dt;
01299
01300 if (aircraft->weapons[k].ammoLeft <= 0)
01301 AII_ReloadWeapon(&aircraft->weapons[k]);
01302 }
01303 }
01304 }
01305 }
01306
01307 if (updateRadarOverlay && radarOverlayReset && MAP_IsRadarOverlayActivated()) {
01308 RADAR_UpdateWholeRadarOverlay();
01309 radarOverlayReset = qfalse;
01310 }
01311 }
01312
01318 aircraft_t* AIR_AircraftGetFromIDX (int idx)
01319 {
01320 int baseIdx;
01321
01322 #ifdef PARANOID
01323 if (ccs.numBases < 1) {
01324 Com_DPrintf(DEBUG_CLIENT, "AIR_AircraftGetFromIDX: no base(s) found!\n");
01325 }
01326 #endif
01327
01328 for (baseIdx = 0; baseIdx < ccs.numBases; baseIdx++) {
01329 base_t *base = B_GetFoundedBaseByIDX(baseIdx);
01330 aircraft_t* aircraft = NULL;
01331
01332 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
01333 if (aircraft->idx == idx) {
01334 Com_DPrintf(DEBUG_CLIENT, "AIR_AircraftGetFromIDX: aircraft idx: %i - base idx: %i (%s)\n",
01335 aircraft->idx, base->idx, base->name);
01336 return aircraft;
01337 }
01338 }
01339 }
01340
01341 return NULL;
01342 }
01343
01350 qboolean AIR_SendAircraftToMission (aircraft_t *aircraft, mission_t *mission)
01351 {
01352 if (!aircraft || !mission)
01353 return qfalse;
01354
01355 if (AIR_GetTeamSize(aircraft) == 0) {
01356 UI_Popup(_("Notice"), _("Assign one or more soldiers to this aircraft first."));
01357 return qfalse;
01358 }
01359
01360
01361 if (AIR_IsAircraftInBase(aircraft)) {
01362
01363 AII_ReloadAircraftWeapons(aircraft);
01364 }
01365
01366
01367 ccs.interceptAircraft = aircraft;
01368
01369
01370
01371 if (aircraft->homebase->baseStatus == BASE_UNDER_ATTACK &&
01372 AIR_IsAircraftInBase(aircraft)) {
01373 aircraft->mission = mission;
01374 mission->active = qtrue;
01375 UI_PushWindow("popup_baseattack", NULL);
01376 return qtrue;
01377 }
01378
01379 if (!AIR_AircraftHasEnoughFuel(aircraft, mission->pos)) {
01380 MS_AddNewMessage(_("Notice"), _("Insufficient fuel."), qfalse, MSG_STANDARD, NULL);
01381 return qfalse;
01382 }
01383
01384 MAP_MapCalcLine(aircraft->pos, mission->pos, &aircraft->route);
01385 aircraft->status = AIR_MISSION;
01386 aircraft->time = 0;
01387 aircraft->point = 0;
01388 aircraft->mission = mission;
01389
01390 return qtrue;
01391 }
01392
01397 static void AII_InitialiseAircraftSlots (aircraft_t *aircraftTemplate)
01398 {
01399 int i;
01400
01401
01402 for (i = 0; i < MAX_AIRCRAFTSLOT; i++) {
01403 AII_InitialiseSlot(aircraftTemplate->weapons + i, aircraftTemplate, NULL, NULL, AC_ITEM_WEAPON);
01404 AII_InitialiseSlot(aircraftTemplate->electronics + i, aircraftTemplate, NULL, NULL, AC_ITEM_ELECTRONICS);
01405 }
01406 AII_InitialiseSlot(&aircraftTemplate->shield, aircraftTemplate, NULL, NULL, AC_ITEM_SHIELD);
01407 }
01408
01413 static const char *air_position_strings[AIR_POSITIONS_MAX] = {
01414 "nose_left",
01415 "nose_center",
01416 "nose_right",
01417 "wing_left",
01418 "wing_right",
01419 "rear_left",
01420 "rear_center",
01421 "rear_right"
01422 };
01423
01427 static const value_t aircraft_param_vals[] = {
01428 {"speed", V_INT, offsetof(aircraft_t, stats[AIR_STATS_SPEED]), MEMBER_SIZEOF(aircraft_t, stats[0])},
01429 {"maxspeed", V_INT, offsetof(aircraft_t, stats[AIR_STATS_MAXSPEED]), MEMBER_SIZEOF(aircraft_t, stats[0])},
01430 {"shield", V_INT, offsetof(aircraft_t, stats[AIR_STATS_SHIELD]), MEMBER_SIZEOF(aircraft_t, stats[0])},
01431 {"ecm", V_INT, offsetof(aircraft_t, stats[AIR_STATS_ECM]), MEMBER_SIZEOF(aircraft_t, stats[0])},
01432 {"damage", V_INT, offsetof(aircraft_t, stats[AIR_STATS_DAMAGE]), MEMBER_SIZEOF(aircraft_t, stats[0])},
01433 {"accuracy", V_INT, offsetof(aircraft_t, stats[AIR_STATS_ACCURACY]), MEMBER_SIZEOF(aircraft_t, stats[0])},
01434 {"antimatter", V_INT, offsetof(aircraft_t, stats[AIR_STATS_ANTIMATTER]), MEMBER_SIZEOF(aircraft_t, stats[0])},
01435
01436 {NULL, 0, 0, 0}
01437 };
01438
01440 static const value_t aircraft_vals[] = {
01441 {"name", V_STRING, offsetof(aircraft_t, name), 0},
01442 {"defaultname", V_TRANSLATION_STRING, offsetof(aircraft_t, defaultName), 0},
01443 {"numteam", V_INT, offsetof(aircraft_t, maxTeamSize), MEMBER_SIZEOF(aircraft_t, maxTeamSize)},
01444 {"size", V_INT, offsetof(aircraft_t, size), MEMBER_SIZEOF(aircraft_t, size)},
01445 {"nogeoscape", V_BOOL, offsetof(aircraft_t, notOnGeoscape), MEMBER_SIZEOF(aircraft_t, notOnGeoscape)},
01446 {"interestlevel", V_INT, offsetof(aircraft_t, ufoInterestOnGeoscape), MEMBER_SIZEOF(aircraft_t, ufoInterestOnGeoscape)},
01447
01448 {"image", V_CLIENT_HUNK_STRING, offsetof(aircraft_t, image), 0},
01449 {"model", V_CLIENT_HUNK_STRING, offsetof(aircraft_t, model), 0},
01450
01451 {"price", V_INT, offsetof(aircraft_t, price), MEMBER_SIZEOF(aircraft_t, price)},
01452
01453
01454 {"building", V_CLIENT_HUNK_STRING, offsetof(aircraft_t, building), 0},
01455
01456 {NULL, 0, 0, 0}
01457 };
01458
01468 void AIR_ParseAircraft (const char *name, const char **text, qboolean assignAircraftItems)
01469 {
01470 const char *errhead = "AIR_ParseAircraft: unexpected end of file (aircraft ";
01471 aircraft_t *aircraftTemplate;
01472 const value_t *vp;
01473 const char *token;
01474 int i;
01475 technology_t *tech;
01476 aircraftItemType_t itemType = MAX_ACITEMS;
01477
01478 if (ccs.numAircraftTemplates >= MAX_AIRCRAFT) {
01479 Com_Printf("AIR_ParseAircraft: too many aircraft definitions; def \"%s\" ignored\n", name);
01480 return;
01481 }
01482
01483 if (!assignAircraftItems) {
01484 for (i = 0; i < ccs.numAircraftTemplates; i++) {
01485 if (!strcmp(ccs.aircraftTemplates[i].id, name)) {
01486 Com_Printf("AIR_ParseAircraft: Second aircraft with same name found (%s) - second ignored\n", name);
01487 return;
01488 }
01489 }
01490
01491
01492 aircraftTemplate = &ccs.aircraftTemplates[ccs.numAircraftTemplates];
01493 memset(aircraftTemplate, 0, sizeof(*aircraftTemplate));
01494
01495 Com_DPrintf(DEBUG_CLIENT, "...found aircraft %s\n", name);
01496 aircraftTemplate->tpl = aircraftTemplate;
01497 aircraftTemplate->id = Mem_PoolStrDup(name, cl_genericPool, 0);
01498 aircraftTemplate->status = AIR_HOME;
01499
01500 aircraftTemplate->ufotype = UFO_MAX;
01501 AII_InitialiseAircraftSlots(aircraftTemplate);
01502
01503 RADAR_InitialiseUFOs(&aircraftTemplate->radar);
01504
01505 ccs.numAircraftTemplates++;
01506 } else {
01507 aircraftTemplate = AIR_GetAircraft(name);
01508
01509 aircraftTemplate->maxWeapons = 0;
01510 aircraftTemplate->maxElectronics = 0;
01511
01512 if (aircraftTemplate->type == AIRCRAFT_UFO)
01513 aircraftTemplate->ufotype = Com_UFOShortNameToID(aircraftTemplate->id);
01514 }
01515
01516
01517 token = Com_Parse(text);
01518
01519 if (!*text || *token != '{') {
01520 Com_Printf("AIR_ParseAircraft: aircraft def \"%s\" without body ignored\n", name);
01521 return;
01522 }
01523
01524 do {
01525 token = Com_EParse(text, errhead, name);
01526 if (!*text)
01527 break;
01528 if (*token == '}')
01529 break;
01530
01531 if (!strcmp(token, "name")) {
01532 token = Com_EParse(text, errhead, name);
01533 if (!*text)
01534 return;
01535 if (token[0] == '_')
01536 token++;
01537 Q_strncpyz(aircraftTemplate->name, token, sizeof(aircraftTemplate->name));
01538 continue;
01539 }
01540 if (assignAircraftItems) {
01541
01542
01543 if (*token == '{') {
01544 FS_SkipBlock(text);
01545 } else if (!strcmp(token, "shield")) {
01546 token = Com_EParse(text, errhead, name);
01547 if (!*text)
01548 return;
01549 Com_DPrintf(DEBUG_CLIENT, "use shield %s for aircraft %s\n", token, aircraftTemplate->id);
01550 tech = RS_GetTechByID(token);
01551 if (tech)
01552 aircraftTemplate->shield.item = INVSH_GetItemByID(tech->provides);
01553 } else if (!strcmp(token, "slot")) {
01554 token = Com_EParse(text, errhead, name);
01555 if (!*text || *token != '{') {
01556 Com_Printf("AIR_ParseAircraft: Invalid slot value for aircraft: %s\n", name);
01557 return;
01558 }
01559 do {
01560 token = Com_EParse(text, errhead, name);
01561 if (!*text)
01562 break;
01563 if (*token == '}')
01564 break;
01565
01566 if (!strcmp(token, "type")) {
01567 token = Com_EParse(text, errhead, name);
01568 if (!*text)
01569 return;
01570 for (i = 0; i < MAX_ACITEMS; i++) {
01571 if (!strcmp(token, air_slot_type_strings[i])) {
01572 itemType = i;
01573 switch (itemType) {
01574 case AC_ITEM_WEAPON:
01575 aircraftTemplate->maxWeapons++;
01576 break;
01577 case AC_ITEM_ELECTRONICS:
01578 aircraftTemplate->maxElectronics++;
01579 break;
01580 default:
01581 itemType = MAX_ACITEMS;
01582 }
01583 break;
01584 }
01585 }
01586 if (i == MAX_ACITEMS)
01587 Com_Error(ERR_DROP, "Unknown value '%s' for slot type\n", token);
01588 } else if (!strcmp(token, "position")) {
01589 token = Com_EParse(text, errhead, name);
01590 if (!*text)
01591 return;
01592 for (i = 0; i < AIR_POSITIONS_MAX; i++) {
01593 if (!strcmp(token, air_position_strings[i])) {
01594 switch (itemType) {
01595 case AC_ITEM_WEAPON:
01596 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].pos = i;
01597 break;
01598 case AC_ITEM_ELECTRONICS:
01599 aircraftTemplate->electronics[aircraftTemplate->maxElectronics - 1].pos = i;
01600 break;
01601 default:
01602 i = AIR_POSITIONS_MAX;
01603 }
01604 break;
01605 }
01606 }
01607 if (i == AIR_POSITIONS_MAX)
01608 Com_Error(ERR_DROP, "Unknown value '%s' for slot position\n", token);
01609 } else if (!strcmp(token, "contains")) {
01610 token = Com_EParse(text, errhead, name);
01611 if (!*text)
01612 return;
01613 tech = RS_GetTechByID(token);
01614 if (tech) {
01615 switch (itemType) {
01616 case AC_ITEM_WEAPON:
01617 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].item = INVSH_GetItemByID(tech->provides);
01618 Com_DPrintf(DEBUG_CLIENT, "use weapon %s for aircraft %s\n", token, aircraftTemplate->id);
01619 break;
01620 case AC_ITEM_ELECTRONICS:
01621 aircraftTemplate->electronics[aircraftTemplate->maxElectronics - 1].item = INVSH_GetItemByID(tech->provides);
01622 Com_DPrintf(DEBUG_CLIENT, "use electronics %s for aircraft %s\n", token, aircraftTemplate->id);
01623 break;
01624 default:
01625 Com_Printf("Ignoring item value '%s' due to unknown slot type\n", token);
01626 }
01627 }
01628 } else if (!strcmp(token, "ammo")) {
01629 token = Com_EParse(text, errhead, name);
01630 if (!*text)
01631 return;
01632 tech = RS_GetTechByID(token);
01633 if (tech) {
01634 if (itemType == AC_ITEM_WEAPON) {
01635 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].ammo = INVSH_GetItemByID(tech->provides);
01636 Com_DPrintf(DEBUG_CLIENT, "use ammo %s for aircraft %s\n", token, aircraftTemplate->id);
01637 } else
01638 Com_Printf("Ignoring ammo value '%s' due to unknown slot type\n", token);
01639 }
01640 } else if (!strcmp(token, "size")) {
01641 token = Com_EParse(text, errhead, name);
01642 if (!*text)
01643 return;
01644 if (itemType == AC_ITEM_WEAPON) {
01645 if (!strcmp(token, "light"))
01646 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].size = ITEM_LIGHT;
01647 else if (!strcmp(token, "medium"))
01648 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].size = ITEM_MEDIUM;
01649 else if (!strcmp(token, "heavy"))
01650 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].size = ITEM_HEAVY;
01651 else
01652 Com_Printf("Unknown size value for aircraft slot: '%s'\n", token);
01653 } else
01654 Com_Printf("Ignoring size parameter '%s' for non-weapon aircraft slots\n", token);
01655 } else
01656 Com_Printf("AIR_ParseAircraft: Ignoring unknown slot value '%s'\n", token);
01657 } while (*text);
01658 }
01659 } else {
01660 if (!strcmp(token, "shield")) {
01661 Com_EParse(text, errhead, name);
01662 continue;
01663 }
01664
01665 for (vp = aircraft_vals; vp->string; vp++)
01666 if (!strcmp(token, vp->string)) {
01667
01668 token = Com_EParse(text, errhead, name);
01669 if (!*text)
01670 return;
01671 switch (vp->type) {
01672 case V_TRANSLATION_STRING:
01673 token++;
01674 case V_CLIENT_HUNK_STRING:
01675 Mem_PoolStrDupTo(token, (char**) ((char*)aircraftTemplate + (int)vp->ofs), cl_genericPool, 0);
01676 break;
01677 default:
01678 Com_EParseValue(aircraftTemplate, token, vp->type, vp->ofs, vp->size);
01679 }
01680
01681 break;
01682 }
01683
01684 if (!strcmp(token, "type")) {
01685 token = Com_EParse(text, errhead, name);
01686 if (!*text)
01687 return;
01688 if (!strcmp(token, "transporter"))
01689 aircraftTemplate->type = AIRCRAFT_TRANSPORTER;
01690 else if (!strcmp(token, "interceptor"))
01691 aircraftTemplate->type = AIRCRAFT_INTERCEPTOR;
01692 else if (!strcmp(token, "ufo"))
01693 aircraftTemplate->type = AIRCRAFT_UFO;
01694 } else if (!strcmp(token, "slot")) {
01695 token = Com_EParse(text, errhead, name);
01696 if (!*text || *token != '{') {
01697 Com_Printf("AIR_ParseAircraft: Invalid slot value for aircraft: %s\n", name);
01698 return;
01699 }
01700 FS_SkipBlock(text);
01701 } else if (!strcmp(token, "param")) {
01702 token = Com_EParse(text, errhead, name);
01703 if (!*text || *token != '{') {
01704 Com_Printf("AIR_ParseAircraft: Invalid param value for aircraft: %s\n", name);
01705 return;
01706 }
01707 do {
01708 token = Com_EParse(text, errhead, name);
01709 if (!*text)
01710 break;
01711 if (*token == '}')
01712 break;
01713
01714 if (!strcmp(token, "range")) {
01715
01716 token = Com_EParse(text, errhead, name);
01717 if (!*text)
01718 return;
01719 Com_EParseValue(aircraftTemplate, token, V_INT, offsetof(aircraft_t, stats[AIR_STATS_FUELSIZE]), MEMBER_SIZEOF(aircraft_t, stats[0]));
01720 if (aircraftTemplate->stats[AIR_STATS_SPEED] == 0)
01721 Com_Error(ERR_DROP, "AIR_ParseAircraft: speed value must be entered before range value");
01722 aircraftTemplate->stats[AIR_STATS_FUELSIZE] = (int) (2.0f * (float)SECONDS_PER_HOUR * aircraftTemplate->stats[AIR_STATS_FUELSIZE]) /
01723 ((float) aircraftTemplate->stats[AIR_STATS_SPEED]);
01724 } else {
01725 for (vp = aircraft_param_vals; vp->string; vp++)
01726 if (!strcmp(token, vp->string)) {
01727
01728 token = Com_EParse(text, errhead, name);
01729 if (!*text)
01730 return;
01731 switch (vp->type) {
01732 case V_TRANSLATION_STRING:
01733 token++;
01734 case V_CLIENT_HUNK_STRING:
01735 Mem_PoolStrDupTo(token, (char**) ((char*)aircraftTemplate + (int)vp->ofs), cl_genericPool, 0);
01736 break;
01737 default:
01738 Com_EParseValue(aircraftTemplate, token, vp->type, vp->ofs, vp->size);
01739 }
01740 break;
01741 }
01742 }
01743 if (!vp->string)
01744 Com_Printf("AIR_ParseAircraft: Ignoring unknown param value '%s'\n", token);
01745 } while (*text);
01746 } else if (!vp->string) {
01747 Com_Printf("AIR_ParseAircraft: unknown token \"%s\" ignored (aircraft %s)\n", token, name);
01748 Com_EParse(text, errhead, name);
01749 }
01750 }
01751 } while (*text);
01752
01753 if (aircraftTemplate->size < AIRCRAFT_SMALL || aircraftTemplate->size > AIRCRAFT_LARGE)
01754 Sys_Error("Invalid aircraft size given for '%s'", aircraftTemplate->id);
01755 }
01756
01757 #ifdef DEBUG
01758 void AIR_ListCraftIndexes_f (void)
01759 {
01760 int i;
01761
01762 Com_Printf("Base\tlocalIDX\t(Craftname)\n");
01763 for (i = 0; i < ccs.numBases; i++) {
01764 const base_t *base = B_GetBaseByIDX(i);
01765 aircraft_t *aircraft;
01766
01767 aircraft = NULL;
01768 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
01769 Com_Printf("%i (%s)\t%i\t(%s)\n", i, base->name, aircraft->idx, aircraft->name);
01770 }
01771 }
01772 }
01773
01777 void AIR_ListAircraftSamples_f (void)
01778 {
01779 int i = 0, max = ccs.numAircraftTemplates;
01780 const value_t *vp;
01781
01782 Com_Printf("%i aircraft\n", max);
01783 if (Cmd_Argc() == 2) {
01784 max = atoi(Cmd_Argv(1));
01785 if (max >= ccs.numAircraftTemplates || max < 0)
01786 return;
01787 i = max - 1;
01788 }
01789 for (; i < max; i++) {
01790 aircraft_t *aircraftTemplate = &ccs.aircraftTemplates[i];
01791 Com_Printf("aircraft: '%s'\n", aircraftTemplate->id);
01792 for (vp = aircraft_vals; vp->string; vp++) {
01793 Com_Printf("..%s: %s\n", vp->string, Com_ValueToStr(aircraftTemplate, vp->type, vp->ofs));
01794 }
01795 for (vp = aircraft_param_vals; vp->string; vp++) {
01796 Com_Printf("..%s: %s\n", vp->string, Com_ValueToStr(aircraftTemplate, vp->type, vp->ofs));
01797 }
01798 }
01799 }
01800 #endif
01801
01802
01803
01804
01805
01810 void AIR_AircraftsNotifyMissionRemoved (const mission_t *const mission)
01811 {
01812 int baseIdx;
01813 aircraft_t* aircraft;
01814
01815
01816 for (baseIdx = 0; baseIdx < ccs.numBases; baseIdx++) {
01817 base_t *base = B_GetFoundedBaseByIDX(baseIdx);
01818
01819 aircraft = NULL;
01820 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
01821 if (aircraft->status == AIR_MISSION && aircraft->mission == mission)
01822 AIR_AircraftReturnToBase(aircraft);
01823 }
01824 }
01825 }
01826
01832 void AIR_AircraftsNotifyUFORemoved (const aircraft_t *const ufo, qboolean destroyed)
01833 {
01834 int baseIdx;
01835 aircraft_t* aircraft;
01836 int i;
01837
01838 assert(ufo);
01839
01840 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
01841 base_t *base = B_GetFoundedBaseByIDX(baseIdx);
01842 if (!base)
01843 continue;
01844
01845
01846 for (i = 0; i < base->numBatteries; i++) {
01847 if (base->batteries[i].target == ufo)
01848 base->batteries[i].target = NULL;
01849 else if (destroyed && (base->batteries[i].target > ufo))
01850 base->batteries[i].target--;
01851 }
01852 for (i = 0; i < base->numLasers; i++) {
01853 if (base->lasers[i].target == ufo)
01854 base->lasers[i].target = NULL;
01855 else if (destroyed && (base->lasers[i].target > ufo))
01856 base->lasers[i].target--;
01857 }
01858
01859 aircraft = NULL;
01860 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL)
01861 if (aircraft->status == AIR_UFO) {
01862 if (ufo == aircraft->aircraftTarget)
01863 AIR_AircraftReturnToBase(aircraft);
01864 else if (destroyed && (ufo < aircraft->aircraftTarget)) {
01865 aircraft->aircraftTarget--;
01866 }
01867 }
01868 }
01869 }
01870
01875 void AIR_AircraftsUFODisappear (const aircraft_t *const ufo)
01876 {
01877 int baseIdx;
01878
01879
01880 for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
01881 const base_t *base = B_GetBaseByIDX(baseIdx);
01882 aircraft_t *aircraft;
01883
01884 aircraft = NULL;
01885 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
01886 if (aircraft->status == AIR_UFO)
01887 if (ufo == aircraft->aircraftTarget)
01888 AIR_AircraftReturnToBase(aircraft);
01889 }
01890 }
01891 }
01892
01903 static inline float AIR_GetDestinationFunction (const float c, const float B, const float speedRatio, float a)
01904 {
01905 return pow(cos(a) - cos(speedRatio * a) * cos(c), 2.)
01906 - sin(c) * sin(c) * (sin(speedRatio * a) * sin(speedRatio * a) - sin(a) * sin(a) * sin(B) * sin(B));
01907 }
01908
01919 static inline float AIR_GetDestinationDerivativeFunction (const float c, const float B, const float speedRatio, float a)
01920 {
01921 return 2. * (cos(a) - cos(speedRatio * a) * cos(c)) * (- sin(a) + speedRatio * sin(speedRatio * a) * cos(c))
01922 - sin(c) * sin(c) * (speedRatio * sin(2. * speedRatio * a) - sin(2. * a) * sin(B) * sin(B));
01923 }
01924
01935 static float AIR_GetDestinationFindRoot (const float c, const float B, const float speedRatio, float start)
01936 {
01937 const float BIG_STEP = .05;
01939 const float PRECISION_ROOT = 0.000001;
01940 const float MAXIMUM_VALUE_ROOT = 2. * M_PI;
01941 float epsilon;
01942 float begin, end, middle;
01943 float fBegin, fEnd, fMiddle;
01944 float fdBegin, fdEnd, fdMiddle;
01946
01947 end = start + PRECISION_ROOT / 10.;
01948 fEnd = AIR_GetDestinationFunction(c, B, speedRatio, end);
01949 fdEnd = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, end);
01950
01951 do {
01952 begin = end;
01953 fBegin = fEnd;
01954 fdBegin = fdEnd;
01955 end = begin + BIG_STEP;
01956 if (end > MAXIMUM_VALUE_ROOT) {
01957 end = MAXIMUM_VALUE_ROOT;
01958 fEnd = AIR_GetDestinationFunction(c, B, speedRatio, end);
01959 break;
01960 }
01961 fEnd = AIR_GetDestinationFunction(c, B, speedRatio, end);
01962 fdEnd = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, end);
01963 } while (fBegin * fEnd > 0 && fdBegin * fdEnd > 0);
01964
01965 if (fBegin * fEnd > 0) {
01966 if (fdBegin * fdEnd < 0) {
01967
01968
01969 middle = (begin + end) / 2.;
01970 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
01971 fdMiddle = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, middle);
01972 do {
01973 if (fdEnd * fdMiddle < 0) {
01974
01975 begin = middle;
01976 fBegin = fMiddle;
01977 fdBegin = fdMiddle;
01978 } else if (fdBegin * fdMiddle < 0) {
01979
01980 end = middle;
01981 fEnd = fMiddle;
01982 fdEnd = fdMiddle;
01983 } else {
01984 Com_Error(ERR_DROP, "AIR_GetDestinationFindRoot: Error in calculation, can't find root");
01985 }
01986 middle = (begin + end) / 2.;
01987 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
01988 fdMiddle = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, middle);
01989
01990 epsilon = end - middle ;
01991
01992 if (epsilon < PRECISION_ROOT) {
01993
01994
01995 return AIR_GetDestinationFindRoot(c, B, speedRatio, end);
01996 }
01997 } while (fBegin * fEnd > 0);
01998 } else {
01999
02000 Com_DPrintf(DEBUG_CLIENT, "AIR_GetDestinationFindRoot: Did not find solution is range %.2f, %.2f\n", start, MAXIMUM_VALUE_ROOT);
02001 return -10.;
02002 }
02003 }
02004
02005
02006
02007 middle = (begin + end) / 2.;
02008 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
02009
02010 do {
02011 if (fEnd * fMiddle < 0) {
02012
02013 begin = middle;
02014 fBegin = fMiddle;
02015 } else if (fBegin * fMiddle < 0) {
02016
02017 end = middle;
02018 fEnd = fMiddle;
02019 } else {
02020 Com_DPrintf(DEBUG_CLIENT, "AIR_GetDestinationFindRoot: Error in calculation, one of the value is nan\n");
02021 return -10.;
02022 }
02023 middle = (begin + end) / 2.;
02024 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
02025
02026 epsilon = end - middle ;
02027 } while (epsilon > PRECISION_ROOT);
02028 return middle;
02029 }
02030
02040 void AIR_GetDestinationWhilePursuing (const aircraft_t const *shooter, const aircraft_t const *target, vec2_t *dest)
02041 {
02042 vec3_t shooterPos, targetPos, targetDestPos, shooterDestPos, rotationAxis;
02043 vec3_t tangentVectTS, tangentVectTD;
02044 float a, b, c, B;
02045
02046 const float speedRatio = (float)(shooter->stats[AIR_STATS_SPEED]) / target->stats[AIR_STATS_SPEED];
02047
02048 c = GetDistanceOnGlobe(shooter->pos, target->pos) * torad;
02049
02050
02051 PolarToVec(shooter->pos, shooterPos);
02052 PolarToVec(target->pos, targetPos);
02053 PolarToVec(target->route.point[target->route.numPoints - 1], targetDestPos);
02054
02072
02073 CrossProduct(targetPos, shooterPos, rotationAxis);
02074 VectorNormalize(rotationAxis);
02075 RotatePointAroundVector(tangentVectTS, rotationAxis, targetPos, 90.0f);
02076
02077 CrossProduct(targetPos, targetDestPos, rotationAxis);
02078 VectorNormalize(rotationAxis);
02079 RotatePointAroundVector(tangentVectTD, rotationAxis, targetPos, 90.0f);
02080
02081
02082 B = acos(DotProduct(tangentVectTS, tangentVectTD));
02083
02084
02085 for (a = 0;;) {
02086 a = AIR_GetDestinationFindRoot(c, B, speedRatio, a);
02087
02088 if (a < 0.) {
02089
02090 break;
02091 }
02092
02093
02094 CrossProduct(targetPos, targetDestPos, rotationAxis);
02095 VectorNormalize(rotationAxis);
02096
02097
02098 RotatePointAroundVector(shooterDestPos, rotationAxis, targetPos, a * todeg);
02099 VecToPolar(shooterDestPos, *dest);
02100
02101 b = GetDistanceOnGlobe(shooter->pos, *dest) * torad;
02102
02103 if (fabs(b - speedRatio * a) < .1)
02104 break;
02105
02106 Com_DPrintf(DEBUG_CLIENT, "AIR_GetDestinationWhilePursuing: reject solution: doesn't fit %.2f == %.2f\n", b, speedRatio * a);
02107 }
02108
02109 if (a < 0.) {
02110
02111 Vector2Copy(target->pos, (*dest));
02112 return;
02113 }
02114
02116
02117 assert((*dest)[0] <= 180.0f && (*dest)[0] >= -180.0f && (*dest)[1] <= 90.0f && (*dest)[1] >= -90.0f);
02118 }
02119
02125 qboolean AIR_SendAircraftPursuingUFO (aircraft_t* aircraft, aircraft_t* ufo)
02126 {
02127 const int num = ufo - ccs.ufos;
02128 vec2_t dest;
02129
02130 if (num < 0 || num >= ccs.numUFOs || !aircraft || !ufo)
02131 return qfalse;
02132
02133
02134 if (AIR_IsAircraftInBase(aircraft)) {
02135
02136 AII_ReloadAircraftWeapons(aircraft);
02137 }
02138
02139 AIR_GetDestinationWhilePursuing(aircraft, ufo, &dest);
02140
02141 if (!AIR_AircraftHasEnoughFuel(aircraft, dest)) {
02142
02143 if (AIR_AircraftHasEnoughFuel(aircraft, ufo->pos)) {
02144 Com_DPrintf(DEBUG_CLIENT, "AIR_SendAircraftPursuingUFO: not enough fuel to anticipate target movement: go directly to target position\n");
02145 Vector2Copy(ufo->pos, dest);
02146 } else {
02147 MS_AddNewMessage(_("Notice"), va(_("Craft %s has not enough fuel to intercept UFO: fly back to %s."), aircraft->name, aircraft->homebase->name), qfalse, MSG_STANDARD, NULL);
02148 AIR_AircraftReturnToBase(aircraft);
02149 return qfalse;
02150 }
02151 }
02152
02153 MAP_MapCalcLine(aircraft->pos, dest, &aircraft->route);
02154 aircraft->status = AIR_UFO;
02155 aircraft->time = 0;
02156 aircraft->point = 0;
02157 aircraft->aircraftTarget = ufo;
02158 return qtrue;
02159 }
02160
02161
02162
02163
02164
02169 void AIR_ResetAircraftTeam (aircraft_t *aircraft)
02170 {
02171 LIST_Delete(&aircraft->acTeam);
02172 }
02173
02180 qboolean AIR_AddToAircraftTeam (aircraft_t *aircraft, employee_t* employee)
02181 {
02182 if (!employee) {
02183 Com_DPrintf(DEBUG_CLIENT, "AIR_AddToAircraftTeam: No employee given!\n");
02184 return qfalse;
02185 }
02186
02187 if (!aircraft) {
02188 Com_DPrintf(DEBUG_CLIENT, "AIR_AddToAircraftTeam: No aircraft given!\n");
02189 return qfalse;
02190 }
02191 if (AIR_GetTeamSize(aircraft) < aircraft->maxTeamSize) {
02192 LIST_AddPointer(&aircraft->acTeam, employee);
02193 Com_DPrintf(DEBUG_CLIENT, "AIR_AddToAircraftTeam: added idx '%d'\n",
02194 employee->idx);
02195 return qtrue;
02196 }
02197
02198 Com_DPrintf(DEBUG_CLIENT, "AIR_AddToAircraftTeam: No space in aircraft\n");
02199 return qfalse;
02200 }
02201
02208 qboolean AIR_RemoveFromAircraftTeam (aircraft_t *aircraft, const employee_t *employee)
02209 {
02210 linkedList_t* l;
02211
02212 assert(aircraft);
02213 assert(employee);
02214
02215 if (AIR_GetTeamSize(aircraft) == 0)
02216 return qfalse;
02217
02218 for (l = aircraft->acTeam; l != NULL; l = l->next) {
02219 const employee_t const *employeeInCraft = (const employee_t *)l->data;
02220
02221 if (employeeInCraft == employee) {
02222 LIST_RemoveEntry(&aircraft->acTeam, l);
02223 Com_DPrintf(DEBUG_CLIENT, "AIR_RemoveFromAircraftTeam: removed idx '%d' \n",
02224 employee->idx);
02225 return qtrue;
02226 }
02227 }
02228
02229
02230 assert(aircraft->homebase);
02231 Com_Printf("AIR_RemoveFromAircraftTeam: error: idx '%d' (type: %i) not on aircraft %i (size: %i) (base: %i) in base %i\n",
02232 employee->idx, employee->type, aircraft->idx, aircraft->maxTeamSize,
02233 AIR_GetAircraftIDXInBase(aircraft), aircraft->homebase->idx);
02234 return qfalse;
02235 }
02236
02243 qboolean AIR_IsInAircraftTeam (const aircraft_t *aircraft, const employee_t *employee)
02244 {
02245 if (!aircraft) {
02246 Com_DPrintf(DEBUG_CLIENT, "AIR_IsInAircraftTeam: No aircraft given\n");
02247 return qfalse;
02248 }
02249
02250 if (!employee) {
02251 Com_Printf("AIR_IsInAircraftTeam: No employee given.\n");
02252 return qfalse;
02253 }
02254
02255 if (AIR_GetTeamSize(aircraft) == 0)
02256 return qfalse;
02257
02258 return LIST_GetPointer(aircraft->acTeam, employee) != NULL;
02259 }
02260
02266 int AIR_GetTeamSize (const aircraft_t *aircraft)
02267 {
02268 assert(aircraft);
02269 return LIST_Count(aircraft->acTeam);
02270 }
02271
02277 void AIR_AutoAddPilotToAircraft (base_t* base, employee_t* pilot)
02278 {
02279 aircraft_t *aircraft;
02280
02281 aircraft = NULL;
02282 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
02283 if (!aircraft->pilot) {
02284 aircraft->pilot = pilot;
02285 break;
02286 }
02287 }
02288 }
02289
02296 void AIR_RemovePilotFromAssignedAircraft (base_t* base, const employee_t* pilot)
02297 {
02298 aircraft_t *aircraft;
02299
02300 aircraft = NULL;
02301 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
02302 if (aircraft->pilot == pilot) {
02303 aircraft->pilot = NULL;
02304 break;
02305 }
02306 }
02307 }
02308
02316 int AIR_GetAircraftWeaponRanges (const aircraftSlot_t *slot, int maxSlot, float *weaponRanges)
02317 {
02318 int idxSlot;
02319 int idxAllWeap;
02320 float allWeaponRanges[MAX_AIRCRAFTSLOT];
02321 int numAllWeaponRanges = 0;
02322 int numUniqueWeaponRanges = 0;
02323
02324 assert(slot);
02325
02326
02327 for (idxSlot = 0; idxSlot < maxSlot; idxSlot++) {
02328 const aircraftSlot_t *weapon = slot + idxSlot;
02329 const objDef_t *ammo = weapon->ammo;
02330
02331 if (!ammo)
02332 continue;
02333
02334 allWeaponRanges[numAllWeaponRanges] = ammo->craftitem.stats[AIR_STATS_WRANGE];
02335 numAllWeaponRanges++;
02336 }
02337
02338 if (numAllWeaponRanges > 0) {
02339
02340 qsort(allWeaponRanges, numAllWeaponRanges, sizeof(allWeaponRanges[0]), Q_FloatSort);
02341
02342 for (idxAllWeap = 0; idxAllWeap < numAllWeaponRanges; idxAllWeap++) {
02343 if (allWeaponRanges[idxAllWeap] != weaponRanges[numUniqueWeaponRanges - 1] || idxAllWeap == 0) {
02344 weaponRanges[numUniqueWeaponRanges] = allWeaponRanges[idxAllWeap];
02345 numUniqueWeaponRanges++;
02346 }
02347 }
02348 }
02349
02350 return numUniqueWeaponRanges;
02351 }
02352
02358 static void AIR_SaveRouteXML (mxml_node_t *node, const mapline_t *route)
02359 {
02360 int j;
02361 mxml_node_t *subnode;
02362
02363 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_ROUTE);
02364 mxml_AddFloatValue(subnode, SAVE_AIRCRAFT_ROUTE_DISTANCE, route->distance);
02365 for (j = 0; j < route->numPoints; j++) {
02366 mxml_AddPos2(subnode, SAVE_AIRCRAFT_ROUTE_POINT, route->point[j]);
02367 }
02368 }
02369
02380 static void AIR_SaveAircraftSlotsXML (const aircraftSlot_t* slot, const int num, mxml_node_t *p, qboolean weapon)
02381 {
02382 int i;
02383 mxml_node_t *sub;
02384
02385 for (i = 0; i < num; i++) {
02386 sub = mxml_AddNode(p, SAVE_AIRCRAFT_SLOT);
02387 AII_SaveOneSlotXML(sub, &slot[i], weapon);
02388 }
02389 }
02390
02397 static qboolean AIR_SaveAircraftXML (mxml_node_t *p, const aircraft_t* const aircraft, qboolean const isUfo)
02398 {
02399 mxml_node_t *node;
02400 mxml_node_t *subnode;
02401 int l;
02402 linkedList_t *k;
02403
02404 Com_RegisterConstList(saveAircraftConstants);
02405
02406 node = mxml_AddNode(p, SAVE_AIRCRAFT_AIRCRAFT);
02407
02408 mxml_AddString(node, SAVE_AIRCRAFT_ID, aircraft->id);
02409 mxml_AddString(node, SAVE_AIRCRAFT_NAME, aircraft->name);
02410
02411 mxml_AddString(node, SAVE_AIRCRAFT_STATUS, Com_GetConstVariable(SAVE_AIRCRAFTSTATUS_NAMESPACE, aircraft->status));
02412 mxml_AddInt(node, SAVE_AIRCRAFT_FUEL, aircraft->fuel);
02413 mxml_AddInt(node, SAVE_AIRCRAFT_DAMAGE, aircraft->damage);
02414 mxml_AddPos3(node, SAVE_AIRCRAFT_POS, aircraft->pos);
02415 mxml_AddPos3(node, SAVE_AIRCRAFT_DIRECTION, aircraft->direction);
02416 mxml_AddInt(node, SAVE_AIRCRAFT_POINT, aircraft->point);
02417 mxml_AddInt(node, SAVE_AIRCRAFT_TIME, aircraft->time);
02418
02419 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_WEAPONS);
02420 AIR_SaveAircraftSlotsXML(aircraft->weapons, aircraft->maxWeapons, subnode, qtrue);
02421 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_SHIELDS);
02422 AIR_SaveAircraftSlotsXML(&aircraft->shield, 1, subnode, qfalse);
02423 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_ELECTRONICS);
02424 AIR_SaveAircraftSlotsXML(aircraft->electronics, aircraft->maxElectronics, subnode, qfalse);
02425
02426 AIR_SaveRouteXML(node, &aircraft->route);
02427
02428 if (isUfo) {
02429 #ifdef DEBUG
02430 if (!aircraft->mission)
02431 Com_Printf("Error: UFO '%s'is not linked to any mission\n", aircraft->id);
02432 #endif
02433 mxml_AddString(node, SAVE_AIRCRAFT_MISSIONID, aircraft->mission->id);
02435 mxml_AddInt(node, SAVE_AIRCRAFT_DETECTIONIDX, aircraft->detectionIdx);
02436 mxml_AddDate(node, SAVE_AIRCRAFT_LASTSPOTTED_DATE, aircraft->lastSpotted.day, aircraft->lastSpotted.sec);
02437 } else {
02438 if (aircraft->status == AIR_MISSION) {
02439 assert(aircraft->mission);
02440 mxml_AddString(node, SAVE_AIRCRAFT_MISSIONID, aircraft->mission->id);
02441 }
02442 if (aircraft->homebase) {
02443 mxml_AddInt(node, SAVE_AIRCRAFT_HOMEBASE, aircraft->homebase->idx);
02444 }
02445 }
02446
02447 if (aircraft->aircraftTarget) {
02448 if (isUfo)
02449 mxml_AddInt(node, SAVE_AIRCRAFT_AIRCRAFTTARGET, aircraft->aircraftTarget->idx);
02450 else
02451 mxml_AddInt(node, SAVE_AIRCRAFT_AIRCRAFTTARGET, aircraft->aircraftTarget - ccs.ufos);
02452 }
02453
02454 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_AIRSTATS);
02455 for (l = 0; l < AIR_STATS_MAX; l++) {
02456 mxml_node_t *statNode;
02457 #ifdef DEBUG
02458
02459 if (!(isUfo && l == AIR_STATS_DAMAGE) && aircraft->stats[l] < 0)
02460 Com_Printf("Warning: ufo '%s' stats %i: %i is smaller than 0\n", aircraft->id, l, aircraft->stats[l]);
02461 #endif
02462 if (aircraft->stats[l] != 0) {
02463 statNode = mxml_AddNode(subnode, SAVE_AIRCRAFT_AIRSTAT);
02464 mxml_AddString(statNode, SAVE_AIRCRAFT_AIRSTATID, Com_GetConstVariable(SAVE_AIRCRAFTSTAT_NAMESPACE, l));
02465 mxml_AddLong(statNode, SAVE_AIRCRAFT_VAL, aircraft->stats[l]);
02466 }
02467 }
02468
02469 mxml_AddBoolValue(node, SAVE_AIRCRAFT_DETECTED, aircraft->detected);
02470 mxml_AddBoolValue(node, SAVE_AIRCRAFT_LANDED, aircraft->landed);
02471
02472 Com_UnregisterConstList(saveAircraftConstants);
02473
02474
02475 if (isUfo)
02476 return qtrue;
02477
02478 mxml_AddInt(node, SAVE_AIRCRAFT_IDX, aircraft->idx);
02479
02480 mxml_AddIntValue(node, SAVE_AIRCRAFT_RADAR_RANGE, aircraft->radar.range);
02481 mxml_AddIntValue(node, SAVE_AIRCRAFT_RADAR_TRACKINGRANGE, aircraft->radar.trackingRange);
02482 mxml_AddInt(node, SAVE_AIRCRAFT_HANGAR, aircraft->hangar);
02483
02484 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_AIRCRAFTTEAM);
02485
02486 for (k = aircraft->acTeam; k != NULL; k = k->next) {
02487 const employee_t const *employee = (const employee_t *)k->data;
02488 mxml_node_t *ssnode = mxml_AddNode(subnode, SAVE_AIRCRAFT_MEMBER);
02489 mxml_AddInt(ssnode, SAVE_AIRCRAFT_TEAM_UCN, employee->chr.ucn);
02490 }
02491
02492 if (aircraft->pilot)
02493 mxml_AddInt(node, SAVE_AIRCRAFT_PILOTUCN, aircraft->pilot->chr.ucn);
02494
02495
02496 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_CARGO);
02497 for (l = 0; l < aircraft->itemTypes; l++) {
02498 mxml_node_t *ssnode = mxml_AddNode(subnode, SAVE_AIRCRAFT_ITEM);
02499 assert(aircraft->itemcargo[l].item);
02500 mxml_AddString(ssnode, SAVE_AIRCRAFT_ITEMID, aircraft->itemcargo[l].item->id);
02501 mxml_AddInt(ssnode, SAVE_AIRCRAFT_AMOUNT, aircraft->itemcargo[l].amount);
02502 }
02503
02504
02505 {
02506 const int alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft);
02507 const aliensTmp_t *cargo = AL_GetAircraftAlienCargo(aircraft);
02508 subnode = mxml_AddNode(node, SAVE_AIRCRAFT_ALIENCARGO);
02509 for (l = 0; l < alienCargoTypes; l++) {
02510 mxml_node_t *ssnode = mxml_AddNode(subnode, SAVE_AIRCRAFT_CARGO);
02511 assert(cargo[l].teamDef);
02512 mxml_AddString(ssnode, SAVE_AIRCRAFT_TEAMDEFID, cargo[l].teamDef->id);
02513 mxml_AddIntValue(ssnode, SAVE_AIRCRAFT_ALIVE, cargo[l].amountAlive);
02514 mxml_AddIntValue(ssnode, SAVE_AIRCRAFT_DEAD, cargo[l].amountDead);
02515 }
02516 }
02517
02518 return qtrue;
02519 }
02520
02527 qboolean AIR_SaveXML (mxml_node_t *parent)
02528 {
02529 int i;
02530 mxml_node_t * node, *snode;
02531
02532
02533 snode = mxml_AddNode(parent, SAVE_AIRCRAFT_PHALANX);
02534 for (i = 0; i < MAX_BASES; i++) {
02535 base_t *base = B_GetFoundedBaseByIDX(i);
02536 aircraft_t *aircraft;
02537
02538 if (!base)
02539 continue;
02540
02541 aircraft = NULL;
02542 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL)
02543 AIR_SaveAircraftXML(snode, aircraft, qfalse);
02544 }
02545
02546
02547 snode = mxml_AddNode(parent, SAVE_AIRCRAFT_UFOS);
02548 for (i = 0; i < MAX_UFOONGEOSCAPE; i++) {
02549 aircraft_t *ufo = UFO_GetByIDX(i);
02550 if (!ufo || (ufo->id == NULL))
02551 continue;
02552 AIR_SaveAircraftXML(snode, ufo, qtrue);
02553 }
02554
02555
02556 node = mxml_AddNode(parent, SAVE_AIRCRAFT_PROJECTILES);
02557 if (!AIRFIGHT_SaveXML(node))
02558 return qfalse;
02559
02560 return qtrue;
02561 }
02562
02573 static void AIR_LoadAircraftSlotsXML (aircraft_t *aircraft, aircraftSlot_t* slot, mxml_node_t *p, qboolean weapon, const int max)
02574 {
02575 mxml_node_t *act;
02576 int i;
02577 for (i = 0, act = mxml_GetNode(p, SAVE_AIRCRAFT_SLOT); act && i <= max; act = mxml_GetNextNode(act, p, SAVE_AIRCRAFT_SLOT), i++) {
02578 slot[i].aircraft = aircraft;
02579 AII_LoadOneSlotXML(act, &slot[i], weapon);
02580 }
02581 if (i > max)
02582 Com_Printf("Error: Trying to assign more than max (%d) Aircraft Slots (cur is %d)\n", max, i);
02583
02584 }
02585
02591 static qboolean AIR_LoadRouteXML (mxml_node_t *p, mapline_t *route)
02592 {
02593 mxml_node_t *actual;
02594 mxml_node_t *snode;
02595 int count = 0;
02596
02597 snode = mxml_GetNode(p, SAVE_AIRCRAFT_ROUTE);
02598 if (!snode)
02599 return qfalse;
02600
02601 for (actual = mxml_GetPos2(snode, SAVE_AIRCRAFT_ROUTE_POINT, route->point[count]); actual && count <= LINE_MAXPTS;
02602 actual = mxml_GetNextPos2(actual, snode, SAVE_AIRCRAFT_ROUTE_POINT, route->point[++count]))
02603 ;
02604 if (count > LINE_MAXPTS) {
02605 Com_Printf("AIR_Load: number of points (%i) for UFO route exceed maximum value (%i)\n", count, LINE_MAXPTS);
02606 return qfalse;
02607 }
02608 route->numPoints = count;
02609 route->distance = mxml_GetFloat(snode, SAVE_AIRCRAFT_ROUTE_DISTANCE, 0.0);
02610 return qtrue;
02611 }
02612
02618 static qboolean AIR_LoadAircraftXML (mxml_node_t *p, aircraft_t *craft)
02619 {
02620 mxml_node_t *snode;
02621 mxml_node_t *ssnode;
02622 const char *statusId;
02623
02624 int tmpInt;
02625 int l, status;
02626 const char *s = mxml_GetString(p, SAVE_AIRCRAFT_ID);
02627 const aircraft_t *crafttype = AIR_GetAircraft(s);
02628
02629
02630 *craft = *crafttype;
02631
02632 tmpInt = mxml_GetInt(p, SAVE_AIRCRAFT_HOMEBASE, MAX_BASES);
02633 craft->homebase = (tmpInt != MAX_BASES) ? B_GetBaseByIDX(tmpInt) : NULL;
02634
02635 Com_RegisterConstList(saveAircraftConstants);
02636
02637 statusId = mxml_GetString(p, SAVE_AIRCRAFT_STATUS);
02638 if (!Com_GetConstIntFromNamespace(SAVE_AIRCRAFTSTATUS_NAMESPACE, statusId, &status)) {
02639 Com_Printf("Invalid aircraft status '%s'\n", statusId);
02640 Com_UnregisterConstList(saveAircraftConstants);
02641 return qfalse;
02642 }
02643
02644 craft->status = status;
02645 craft->fuel = mxml_GetInt(p, SAVE_AIRCRAFT_FUEL, 0);
02646 craft->damage = mxml_GetInt(p, SAVE_AIRCRAFT_DAMAGE, 0);
02647 mxml_GetPos3(p, SAVE_AIRCRAFT_POS, craft->pos);
02648
02649 mxml_GetPos3(p, SAVE_AIRCRAFT_DIRECTION, craft->direction);
02650 craft->point = mxml_GetInt(p, SAVE_AIRCRAFT_POINT, 0);
02651 craft->time = mxml_GetInt(p, SAVE_AIRCRAFT_TIME, 0);
02652
02653 if (!AIR_LoadRouteXML(p, &craft->route)) {
02654 Com_UnregisterConstList(saveAircraftConstants);
02655 return qfalse;
02656 }
02657
02658 s = mxml_GetString(p, SAVE_AIRCRAFT_NAME);
02659 if (s[0] == '\0')
02660 s = _(craft->defaultName);
02661 Q_strncpyz(craft->name, s, sizeof(craft->name));
02662
02663 s = mxml_GetString(p, SAVE_AIRCRAFT_MISSIONID);
02664 if (s[0] == '\0' && !craft->homebase) {
02665 Com_Printf("Error: UFO '%s' is not linked to any mission\n", craft->id);
02666 Com_UnregisterConstList(saveAircraftConstants);
02667 return qfalse;
02668 }
02669 craft->missionID = Mem_PoolStrDup(s, cp_campaignPool, 0);
02670
02671 if (!craft->homebase) {
02672 craft->idx = ccs.numUFOs;
02673
02674 craft->detectionIdx = mxml_GetInt(p, SAVE_AIRCRAFT_DETECTIONIDX, 0);
02675 mxml_GetDate(p, SAVE_AIRCRAFT_LASTSPOTTED_DATE, &craft->lastSpotted.day, &craft->lastSpotted.sec);
02676 }
02677
02678 snode = mxml_GetNode(p, SAVE_AIRCRAFT_AIRSTATS);
02679 for (ssnode = mxml_GetNode(snode, SAVE_AIRCRAFT_AIRSTAT); ssnode; ssnode = mxml_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_AIRSTAT)) {
02680 const char *statId = mxml_GetString(ssnode, SAVE_AIRCRAFT_AIRSTATID);
02681 int idx;
02682
02683 if (!Com_GetConstIntFromNamespace(SAVE_AIRCRAFTSTAT_NAMESPACE, statId, &idx)) {
02684 Com_Printf("Invaild aircraft stat '%s'\n", statId);
02685 Com_UnregisterConstList(saveAircraftConstants);
02686 return qfalse;
02687 }
02688 craft->stats[idx] = mxml_GetLong(ssnode, SAVE_AIRCRAFT_VAL, 0);
02689 #ifdef DEBUG
02690
02691 if (!(!craft->homebase && idx == AIR_STATS_DAMAGE) && craft->stats[idx] < 0)
02692 Com_Printf("Warning: ufo '%s' stats %i: %i is smaller than 0\n", craft->id, idx, craft->stats[idx]);
02693 #endif
02694 }
02695
02696 craft->detected = mxml_GetBool(p, SAVE_AIRCRAFT_DETECTED, qfalse);
02697 craft->landed = mxml_GetBool(p, SAVE_AIRCRAFT_LANDED, qfalse);
02698
02699 tmpInt = mxml_GetInt(p, SAVE_AIRCRAFT_AIRCRAFTTARGET, -1);
02700 if (tmpInt == -1)
02701 craft->aircraftTarget = NULL;
02702 else if (!craft->homebase)
02703 craft->aircraftTarget = AIR_AircraftGetFromIDX(tmpInt);
02704 else
02705 craft->aircraftTarget = ccs.ufos + tmpInt;
02706
02707
02708 snode = mxml_GetNode(p, SAVE_AIRCRAFT_WEAPONS);
02709 AIR_LoadAircraftSlotsXML(craft, craft->weapons, snode, qtrue, craft->maxWeapons);
02710 snode = mxml_GetNode(p, SAVE_AIRCRAFT_SHIELDS);
02711 AIR_LoadAircraftSlotsXML(craft, &craft->shield, snode, qfalse, 1);
02712 snode = mxml_GetNode(p, SAVE_AIRCRAFT_ELECTRONICS);
02713 AIR_LoadAircraftSlotsXML(craft, craft->electronics, snode, qfalse, craft->maxElectronics);
02714
02715 Com_UnregisterConstList(saveAircraftConstants);
02716
02717
02718 if (!craft->homebase)
02719 return qtrue;
02720
02721 craft->idx = mxml_GetInt(p, SAVE_AIRCRAFT_IDX, -1);
02722 if (craft->idx == -1)
02723 return qfalse;
02724 craft->hangar = mxml_GetInt(p, SAVE_AIRCRAFT_HANGAR, 0);
02725
02726 snode = mxml_GetNode(p, SAVE_AIRCRAFT_AIRCRAFTTEAM);
02727 for (ssnode = mxml_GetNode(snode, SAVE_AIRCRAFT_MEMBER); AIR_GetTeamSize(craft) < craft->maxTeamSize && ssnode;
02728 ssnode = mxml_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_MEMBER)) {
02729 const int ucn = mxml_GetInt(ssnode, SAVE_AIRCRAFT_TEAM_UCN, -1);
02730 if (ucn != -1)
02731 LIST_AddPointer(&craft->acTeam, E_GetEmployeeFromChrUCN(ucn));
02732 }
02733
02734 tmpInt = mxml_GetInt(p, SAVE_AIRCRAFT_PILOTUCN, -1);
02735
02736
02737
02738 if (tmpInt != -1)
02739 craft->pilot = E_GetEmployeeFromChrUCN(tmpInt);
02740 else
02741 craft->pilot = NULL;
02742
02743 RADAR_InitialiseUFOs(&craft->radar);
02744 craft->radar.range = mxml_GetInt(p, SAVE_AIRCRAFT_RADAR_RANGE, 0);
02745 craft->radar.trackingRange = mxml_GetInt(p, SAVE_AIRCRAFT_RADAR_TRACKINGRANGE, 0);
02746
02747
02748 snode = mxml_GetNode(p, SAVE_AIRCRAFT_CARGO);
02749 for (l = 0, ssnode = mxml_GetNode(snode, SAVE_AIRCRAFT_ITEM); l < MAX_CARGO && ssnode;
02750 l++, ssnode = mxml_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_ITEM)) {
02751 const char *const str = mxml_GetString(ssnode, SAVE_AIRCRAFT_ITEMID);
02752 const objDef_t *od = INVSH_GetItemByID(str);
02753
02754 if (!od) {
02755 Com_Printf("AIR_LoadAircraftXML: Could not find aircraftitem '%s'\n", str);
02756 l--;
02757 continue;
02758 }
02759
02760 craft->itemcargo[l].item = od;
02761 craft->itemcargo[l].amount = mxml_GetInt(ssnode, SAVE_AIRCRAFT_AMOUNT, 0);
02762 }
02763 craft->itemTypes = l;
02764
02765
02766 snode = mxml_GetNode(p, SAVE_AIRCRAFT_ALIENCARGO);
02767 for (l = 0, ssnode = mxml_GetNode(snode, SAVE_AIRCRAFT_CARGO); l < MAX_CARGO && ssnode;
02768 l++, ssnode = mxml_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_CARGO)) {
02769 aliensTmp_t *cargo = AL_GetAircraftAlienCargo(craft);
02770 const char *const str = mxml_GetString(ssnode, SAVE_AIRCRAFT_TEAMDEFID);
02771
02772 cargo[l].teamDef = Com_GetTeamDefinitionByID(str);
02773 if (!cargo[l].teamDef) {
02774 Com_Printf("AIR_LoadAircraftXML: Could not find teamDef '%s'\n", str);
02775 l--;
02776 continue;
02777 }
02778
02779 cargo[l].amountAlive = mxml_GetInt(ssnode, SAVE_AIRCRAFT_ALIVE, 0);
02780 cargo[l].amountDead = mxml_GetInt(ssnode, SAVE_AIRCRAFT_DEAD, 0);
02781 }
02782 AL_SetAircraftAlienCargoTypes(craft, l);
02783
02784 return qtrue;
02785 }
02786
02791 static void AIR_CorrectAircraftSlotPointers (aircraft_t *aircraft)
02792 {
02793 int i;
02794
02795 assert(aircraft);
02796
02797 for (i = 0; i < aircraft->maxWeapons; i++) {
02798 aircraft->weapons[i].aircraft = aircraft;
02799 aircraft->weapons[i].base = NULL;
02800 aircraft->weapons[i].installation = NULL;
02801 }
02802 for (i = 0; i < aircraft->maxElectronics; i++) {
02803 aircraft->electronics[i].aircraft = aircraft;
02804 aircraft->electronics[i].base = NULL;
02805 aircraft->electronics[i].installation = NULL;
02806 }
02807 aircraft->shield.aircraft = aircraft;
02808 aircraft->shield.base = NULL;
02809 aircraft->shield.installation = NULL;
02810 }
02811
02812 qboolean AIR_LoadXML (mxml_node_t *parent)
02813 {
02814 mxml_node_t *snode, *ssnode;
02815 mxml_node_t *projectiles;
02816 int i;
02817
02818
02819 snode = mxml_GetNode(parent, SAVE_AIRCRAFT_PHALANX);
02820 for (i = 0, ssnode = mxml_GetNode(snode, SAVE_AIRCRAFT_AIRCRAFT); ssnode;
02821 ssnode = mxml_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_AIRCRAFT), i++) {
02822 aircraft_t craft;
02823 if (!AIR_LoadAircraftXML(ssnode, &craft))
02824 return qfalse;
02825 assert(craft.homebase);
02826 AIR_CorrectAircraftSlotPointers(AIR_Add(craft.homebase, &craft));
02827 }
02828 ccs.numAircraft = i;
02829
02830
02831 snode = mxml_GetNode(parent, SAVE_AIRCRAFT_UFOS);
02832
02833 for (i = 0, ssnode = mxml_GetNode(snode, SAVE_AIRCRAFT_AIRCRAFT); i < MAX_UFOONGEOSCAPE && ssnode;
02834 ssnode = mxml_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_AIRCRAFT), i++) {
02835 if (!AIR_LoadAircraftXML(ssnode, &ccs.ufos[i]))
02836 return qfalse;
02837 ccs.numUFOs++;
02838 }
02839
02840
02841 projectiles = mxml_GetNode(parent, SAVE_AIRCRAFT_PROJECTILES);
02842 if (!AIRFIGHT_LoadXML(projectiles))
02843 return qfalse;
02844
02845
02846 for (i = ccs.numUFOs - 1; i >= 0; i--) {
02847 aircraft_t *ufo = UFO_GetByIDX(i);
02848 if (ufo->time < 0 || ufo->stats[AIR_STATS_SPEED] <= 0) {
02849 Com_Printf("AIR_Load: Found invalid ufo entry - remove it - time: %i - speed: %i\n",
02850 ufo->time, ufo->stats[AIR_STATS_SPEED]);
02851 UFO_RemoveFromGeoscape(ufo);
02852 }
02853 }
02854
02855 return qtrue;
02856 }
02857
02861 static void AIR_PostLoadInitMissions (void)
02862 {
02863 int i;
02864
02865
02866 for (i = 0; i < ccs.numBases; i++) {
02867 const base_t *base = B_GetFoundedBaseByIDX(i);
02868 aircraft_t *aircraft = NULL;
02869
02870 while ((aircraft = AIR_GetNextFromBase(base, aircraft))) {
02871 if (!aircraft->missionID || aircraft->missionID[0] == '\0')
02872 continue;
02873 aircraft->mission = CP_GetMissionByID(aircraft->missionID);
02874 Mem_Free(aircraft->missionID);
02875 aircraft->missionID = NULL;
02876 }
02877 }
02878
02879
02880 for (i = 0; i < ccs.numUFOs; i++) {
02881 aircraft_t *aircraft = UFO_GetByIDX(i);
02882
02883 if (!aircraft || !aircraft->missionID || aircraft->missionID[0] == '\0')
02884 continue;
02885 aircraft->mission = CP_GetMissionByID(aircraft->missionID);
02886 Mem_Free(aircraft->missionID);
02887 aircraft->missionID = NULL;
02888 }
02889 }
02890
02895 void AIR_PostLoadInit (void)
02896 {
02897 AIR_PostLoadInitMissions();
02898 }
02899
02905 qboolean AIR_AircraftAllowed (const base_t* base)
02906 {
02907 return B_GetBuildingStatus(base, B_HANGAR) || B_GetBuildingStatus(base, B_SMALL_HANGAR);
02908 }
02909
02914 qboolean AIR_CanIntercept (const aircraft_t *aircraft)
02915 {
02916
02917 if (aircraft->size == AIRCRAFT_SMALL && !B_GetBuildingStatus(aircraft->homebase, B_SMALL_HANGAR))
02918 return qfalse;
02919 if (aircraft->size == AIRCRAFT_LARGE && !B_GetBuildingStatus(aircraft->homebase, B_HANGAR))
02920 return qfalse;
02921
02922
02923 if (!aircraft->pilot)
02924 return qfalse;
02925
02926 return qtrue;
02927 }
02928
02933 qboolean AIR_ScriptSanityCheck (void)
02934 {
02935 int i, j, k, error = 0;
02936 int var;
02937 aircraft_t* a;
02938
02939 for (i = 0, a = ccs.aircraftTemplates; i < ccs.numAircraftTemplates; i++, a++) {
02940 if (a->name[0] == '\0') {
02941 error++;
02942 Com_Printf("...... aircraft '%s' has no name\n", a->id);
02943 }
02944 if (!a->defaultName) {
02945 error++;
02946 Com_Printf("...... aircraft '%s' has no defaultName\n", a->id);
02947 }
02948
02949
02950 for (j = 0; j < a->maxWeapons - 1; j++)
02951 if (a->weapons[j].item && AII_GetItemWeightBySize(a->weapons[j].item) > a->weapons[j].size) {
02952 error++;
02953 Com_Printf("...... aircraft '%s' has an item (%s) too heavy for its slot\n", a->id, a->weapons[j].item->id);
02954 }
02955
02956
02957 if (a->type != AIRCRAFT_UFO) {
02958 for (j = 0; j < a->maxWeapons - 1; j++) {
02959 var = a->weapons[j].pos;
02960 for (k = j + 1; k < a->maxWeapons; k++)
02961 if (var == a->weapons[k].pos) {
02962 error++;
02963 Com_Printf("...... aircraft '%s' has 2 weapons slots at the same location\n", a->id);
02964 }
02965 }
02966 for (j = 0; j < a->maxElectronics - 1; j++) {
02967 var = a->electronics[j].pos;
02968 for (k = j + 1; k < a->maxElectronics; k++)
02969 if (var == a->electronics[k].pos) {
02970 error++;
02971 Com_Printf("...... aircraft '%s' has 2 electronics slots at the same location\n", a->id);
02972 }
02973 }
02974 }
02975 }
02976
02977 return !error;
02978 }
02979
02988 int AIR_CalculateHangarStorage (const aircraft_t *aircraftTemplate, const base_t *base, int used)
02989 {
02990 assert(base);
02991 assert(aircraftTemplate);
02992 assert(aircraftTemplate == aircraftTemplate->tpl);
02993
02994 if (!base->founded)
02995 return -1;
02996 else {
02997 const int aircraftCapacity = AIR_GetCapacityByAircraftWeight(aircraftTemplate);
02998
02999
03000 const int freespace = base->capacities[aircraftCapacity].max - base->capacities[aircraftCapacity].cur - used;
03001 return max(freespace, 0);
03002 }
03003 }
03004
03012 qboolean AIR_RemoveEmployee (employee_t *employee, aircraft_t *aircraft)
03013 {
03014 if (!employee)
03015 return qfalse;
03016
03017
03018
03019 if (!aircraft) {
03020 int i;
03021
03022 for (i = 0; i < ccs.numBases; i++) {
03023 base_t *base = B_GetFoundedBaseByIDX(i);
03024 aircraft_t *acTemp = NULL;
03025
03026 if (!base)
03027 continue;
03028
03029 while ((acTemp = AIR_GetNextFromBase(base, acTemp))) {
03030 if (AIR_IsEmployeeInAircraft(employee, acTemp)) {
03031 aircraft = acTemp;
03032 break;
03033 }
03034 }
03035 if (aircraft)
03036 break;
03037 }
03038 if (!aircraft)
03039 return qfalse;
03040 }
03041
03042 Com_DPrintf(DEBUG_CLIENT, "AIR_RemoveEmployee: base: %i - aircraft->idx: %i\n",
03043 aircraft->homebase ? aircraft->homebase->idx : -1, aircraft->idx);
03044
03045 cls.i.DestroyInventory(&cls.i, &employee->chr.i);
03046 return AIR_RemoveFromAircraftTeam(aircraft, employee);
03047 }
03048
03056 const aircraft_t *AIR_IsEmployeeInAircraft (const employee_t *employee, const aircraft_t* aircraft)
03057 {
03058 if (!employee)
03059 return NULL;
03060
03061 if (employee->transfer)
03062 return NULL;
03063
03064
03065 if (!aircraft) {
03066 int i;
03067 for (i = 0; i < ccs.numBases; i++) {
03068 base_t *base = B_GetFoundedBaseByIDX(i);
03069 aircraft_t *aircraftByIDX = NULL;
03070
03071 if (!base)
03072 continue;
03073
03074 while ((aircraftByIDX = AIR_GetNextFromBase(base, aircraftByIDX))) {
03075 if (AIR_IsEmployeeInAircraft(employee, aircraftByIDX))
03076 return aircraftByIDX;
03077 }
03078 }
03079 return NULL;
03080 }
03081
03082 if (employee->type == EMPL_PILOT) {
03083 if (aircraft->pilot == employee)
03084 return aircraft;
03085 return NULL;
03086 }
03087
03088 if (AIR_IsInAircraftTeam(aircraft, employee))
03089 return aircraft;
03090 else
03091 return NULL;
03092 }
03093
03099 void AIR_RemoveEmployees (aircraft_t *aircraft)
03100 {
03101 linkedList_t* l;
03102
03103 if (!aircraft)
03104 return;
03105
03106 for (l = aircraft->acTeam; l != NULL;) {
03107 employee_t *employee = (employee_t *)l->data;
03108 l = l->next;
03109
03110 AIR_RemoveEmployee(employee, aircraft);
03111 }
03112
03113
03114 aircraft->pilot = NULL;
03115
03116 if (AIR_GetTeamSize(aircraft) > 0)
03117 Com_Error(ERR_DROP, "AIR_RemoveEmployees: Error, there went something wrong with soldier-removing from aircraft.");
03118 }
03119
03120
03126 void AIR_MoveEmployeeInventoryIntoStorage (const aircraft_t *aircraft, equipDef_t *ed)
03127 {
03128 containerIndex_t container;
03129 linkedList_t *l;
03130
03131 if (!aircraft) {
03132 Com_Printf("AIR_MoveEmployeeInventoryIntoStorage: Warning: Called with no aircraft (and thus no carried equipment to add).\n");
03133 return;
03134 }
03135 if (!ed) {
03136 Com_Printf("AIR_MoveEmployeeInventoryIntoStorage: Warning: Called with no equipment definition at add stuff to.\n");
03137 return;
03138 }
03139
03140 if (AIR_GetTeamSize(aircraft) == 0) {
03141 Com_DPrintf(DEBUG_CLIENT, "AIR_MoveEmployeeInventoryIntoStorage: No team to remove equipment from.\n");
03142 return;
03143 }
03144
03145 for (container = 0; container < csi.numIDs; container++) {
03146 for (l = aircraft->acTeam; l != NULL; l = l->next) {
03147 employee_t *employee = (employee_t *)l->data;
03148 if (employee != NULL) {
03149 character_t *chr = &employee->chr;
03150 invList_t *ic = CONTAINER(chr, container);
03151 #if 0
03152 if (INVDEF(container)->temp)
03153 continue;
03154 #endif
03155 while (ic) {
03156 const item_t item = ic->item;
03157 const objDef_t *type = item.t;
03158 invList_t *next = ic->next;
03159
03160 ed->numItems[type->idx]++;
03161 if (item.a) {
03162 assert(type->reload);
03163 assert(item.m);
03164 ed->numItemsLoose[item.m->idx] += item.a;
03165
03166 if (ed->numItemsLoose[item.m->idx] >= type->ammo) {
03167 ed->numItemsLoose[item.m->idx] -= type->ammo;
03168 ed->numItems[item.m->idx]++;
03169 }
03170 }
03171 ic = next;
03172 }
03173 }
03174 }
03175 }
03176 }
03177
03186 static qboolean AIR_AddEmployee (employee_t *employee, aircraft_t *aircraft)
03187 {
03188 if (!employee || !aircraft)
03189 return qfalse;
03190
03191 if (AIR_GetTeamSize(aircraft) < aircraft->maxTeamSize) {
03192
03193 if (AIR_IsEmployeeInAircraft(employee, NULL))
03194 return qfalse;
03195
03196
03197 return AIR_AddToAircraftTeam(aircraft, employee);
03198 }
03199 return qfalse;
03200 }
03201
03208 void AIM_AddEmployeeFromMenu (aircraft_t *aircraft, const int num)
03209 {
03210 employee_t *employee;
03211
03212 Com_DPrintf(DEBUG_CLIENT, "AIM_AddEmployeeFromMenu: Trying to get employee with hired-idx %i.\n", num);
03213
03214
03215 employee = E_GetEmployeeByMenuIndex(num);
03216 if (!employee)
03217 Com_Error(ERR_DROP, "AIM_AddEmployeeFromMenu: Could not get employee %i", num);
03218
03219 Com_DPrintf(DEBUG_CLIENT, "AIM_AddEmployeeFromMenu: employee with idx %i selected\n", employee->idx);
03220
03221 assert(aircraft);
03222
03223 if (AIR_IsEmployeeInAircraft(employee, aircraft)) {
03224
03225 AIR_RemoveEmployee(employee, aircraft);
03226 } else {
03227
03228 AIR_AddEmployee(employee, aircraft);
03229 }
03230 }
03231
03236 void AIR_AssignInitial (aircraft_t *aircraft)
03237 {
03238 int i, num;
03239 base_t *base;
03240
03241 if (!aircraft) {
03242 Com_Printf("AIR_AssignInitial: No aircraft given\n");
03243 return;
03244 }
03245
03246 base = aircraft->homebase;
03247 assert(base);
03248
03249 num = min(E_GenerateHiredEmployeesList(base), aircraft->maxTeamSize);
03250 for (i = 0; i < num; i++)
03251 AIM_AddEmployeeFromMenu(aircraft, i);
03252 }
03253