Bug Summary

File:client/cgame/campaign/cp_aircraft.cpp
Location:line 2022, column 2
Description:The left operand of '<=' is a garbage value

Annotated Source Code

1/**
2 * @file
3 * @brief Most of the aircraft related stuff.
4 * @sa cl_airfight.c
5 * @note Aircraft management functions prefix: AIR_
6 * @note Aircraft menu(s) functions prefix: AIM_
7 * @note Aircraft equipment handling functions prefix: AII_
8 */
9
10/*
11Copyright (C) 2002-2012 UFO: Alien Invasion.
12
13This program is free software; you can redistribute it and/or
14modify it under the terms of the GNU General Public License
15as published by the Free Software Foundation; either version 2
16of the License, or (at your option) any later version.
17
18This program is distributed in the hope that it will be useful,
19but WITHOUT ANY WARRANTY; without even the implied warranty of
20MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21
22See the GNU General Public License for more details.
23
24You should have received a copy of the GNU General Public License
25along with this program; if not, write to the Free Software
26Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27*/
28
29#include "../../cl_shared.h"
30#include "../../ui/ui_dataids.h"
31#include "../../../shared/parse.h"
32#include "cp_campaign.h"
33#include "cp_mapfightequip.h"
34#include "cp_map.h"
35#include "cp_ufo.h"
36#include "cp_alienbase.h"
37#include "cp_time.h"
38#include "cp_missions.h"
39#include "cp_aircraft_callbacks.h"
40#include "save/save_aircraft.h"
41#include "cp_popup.h"
42
43/**
44 * @brief Iterates through the aircraft of a base
45 * @param[in] b The base to get the craft from
46 */
47aircraft_t* AIR_GetFirstFromBase (const base_t *b)
48{
49 if (b) {
50 AIR_ForeachFromBase(aircraft, b)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((b)) && (aircraft)->status != AIR_CRASHED
)) continue; else
51 return aircraft;
52 }
53
54 return NULL__null;
55}
56
57/**
58 * @brief Checks whether there is any aircraft assigned to the given base
59 * @param[in] base The base to check
60 * @return @c true if there is at least one aircraft, @c false otherwise (or when the @c base pointer is @c NULL)
61 */
62bool AIR_BaseHasAircraft (const base_t *base)
63{
64 return base != NULL__null && AIR_GetFirstFromBase(base) != NULL__null;
65}
66
67/**
68 * @brief Returns the number of aircraft on the given base
69 * @param[in] base The base to check
70 */
71int AIR_BaseCountAircraft (const base_t *base)
72{
73 int count = 0;
74
75 AIR_ForeachFromBase(aircraft, base)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((base)) && (aircraft)->status != AIR_CRASHED
)) continue; else
{
76 count++;
77 }
78
79 return count;
80}
81
82/**
83 * @brief Updates hangar capacities for one aircraft in given base.
84 * @param[in] aircraftTemplate Aircraft template.
85 * @param[in,out] base Pointer to base.
86 * @return AIRCRAFT_HANGAR_BIG if aircraft was placed in big hangar
87 * @return AIRCRAFT_HANGAR_SMALL if small
88 * @return AIRCRAFT_HANGAR_ERROR if error or not possible
89 * @sa AIR_NewAircraft
90 * @sa AIR_UpdateHangarCapForAll
91 */
92static int AIR_UpdateHangarCapForOne (const aircraft_t *aircraftTemplate, base_t *base)
93{
94 const baseCapacities_t capType = AIR_GetCapacityByAircraftWeight(aircraftTemplate);
95 const buildingType_t buildingType = B_GetBuildingTypeByCapacity(capType);
96 int freeSpace;
97
98 if (!base || capType == MAX_CAP || buildingType == MAX_BUILDING_TYPE)
99 return AIRCRAFT_HANGAR_ERROR;
100
101 freeSpace = CAP_GetFreeCapacity(base, capType);
102 if (!B_GetBuildingStatus(base, buildingType) || freeSpace < 1) {
103 Com_Printf("AIR_UpdateHangarCapForOne: base '%s' does not have enough hangar space for '%s'!\n", base->name, aircraftTemplate->id);
104 return AIRCRAFT_HANGAR_ERROR;
105 }
106
107 CAP_AddCurrent(base, capType, 1);
108 switch (capType) {
109 case CAP_AIRCRAFT_SMALL:
110 return AIRCRAFT_HANGAR_SMALL;
111 case CAP_AIRCRAFT_BIG:
112 return AIRCRAFT_HANGAR_BIG;
113 default:
114 return AIRCRAFT_HANGAR_ERROR;
115 }
116}
117
118/**
119 * @brief Updates current capacities for hangars in given base.
120 * @param[in,out] base The base we want to update.
121 * @note Call this function whenever you sell/loss aircraft in given base.
122 * @sa BS_SellAircraft_f
123 */
124void AIR_UpdateHangarCapForAll (base_t *base)
125{
126 if (!base)
127 return;
128
129 /* Reset current capacities for hangar. */
130 base->capacities[CAP_AIRCRAFT_BIG].cur = 0;
131 base->capacities[CAP_AIRCRAFT_SMALL].cur = 0;
132
133 AIR_ForeachFromBase(aircraft, base)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((base)) && (aircraft)->status != AIR_CRASHED
)) continue; else
{
134 AIR_UpdateHangarCapForOne(aircraft->tpl, base);
135 }
136}
137
138#ifdef DEBUG1
139/**
140 * @brief Debug function which lists all aircraft in all bases.
141 * @note Use with baseIdx as a parameter to display all aircraft in given base.
142 * @note called with debug_listaircraft
143 */
144void AIR_ListAircraft_f (void)
145{
146 base_t *base = NULL__null;
147
148 if (Cmd_Argc() == 2) {
149 int baseIdx = atoi(Cmd_Argv(1));
150 base = B_GetFoundedBaseByIDX(baseIdx);
151 }
152
153 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
154 int k;
155
156 if (base && aircraft->homebase != base)
157 continue;
158
159 Com_Printf("Aircraft %s\n", aircraft->name);
160 Com_Printf("...idx global %i\n", aircraft->idx);
161 Com_Printf("...homebase: %s\n", aircraft->homebase ? aircraft->homebase->name : "NO HOMEBASE");
162 for (k = 0; k < aircraft->maxWeapons; k++) {
163 aircraftSlot_t *slot = &aircraft->weapons[k];
164 if (slot->item) {
165 Com_Printf("...weapon slot %i contains %s", k, slot->item->id);
166
167 if (!slot->installationTime) {
168 Com_Printf(" (functional)\n");
169 } else if (slot->installationTime > 0) {
170 Com_Printf(" (%i hours before installation is finished)\n", slot->installationTime);
171 } else {
172 Com_Printf(" (%i hours before removing is finished)\n", slot->installationTime);
173 }
174
175 if (slot->ammo) {
176 if (slot->ammoLeft > 1) {
177 Com_Printf("......this weapon is loaded with ammo %s\n", slot->ammo->id);
178 } else {
179 Com_Printf("......no more ammo (%s)\n", slot->ammo->id);
180 }
181 } else {
182 Com_Printf("......this weapon isn't loaded with ammo\n");
183 }
184 } else {
185 Com_Printf("...weapon slot %i is empty\n", k);
186 }
187 }
188
189 if (aircraft->shield.item) {
190 Com_Printf("...armour slot contains %s", aircraft->shield.item->id);
191 if (!aircraft->shield.installationTime) {
192 Com_Printf(" (functional)\n");
193 } else if (aircraft->shield.installationTime > 0) {
194 Com_Printf(" (%i hours before installation is finished)\n", aircraft->shield.installationTime);
195 } else {
196 Com_Printf(" (%i hours before removing is finished)\n", aircraft->shield.installationTime);
197 }
198 } else {
199 Com_Printf("...armour slot is empty\n");
200 }
201
202 for (k = 0; k < aircraft->maxElectronics; k++) {
203 aircraftSlot_t *slot = &aircraft->weapons[k];
204 if (slot->item) {
205 Com_Printf("...electronics slot %i contains %s", k, slot->item->id);
206
207 if (!slot->installationTime) {
208 Com_Printf(" (functional)\n");
209 } else if (slot->installationTime > 0) {
210 Com_Printf(" (%i hours before installation is finished)\n", slot->installationTime);
211 } else {
212 Com_Printf(" (%i hours before removing is finished)\n", slot->installationTime);
213 }
214 } else {
215 Com_Printf("...electronics slot %i is empty\n", k);
216 }
217 }
218
219 if (aircraft->pilot) {
220 character_t *chr = &aircraft->pilot->chr;
221 Com_Printf("...pilot: ucn: %i name: %s\n", chr->ucn, chr->name);
222 } else {
223 Com_Printf("...no pilot assigned\n");
224 }
225
226 Com_Printf("...damage: %i\n", aircraft->damage);
227 Com_Printf("...stats: ");
228 for (k = 0; k < AIR_STATS_MAX; k++) {
229 if (k == AIR_STATS_WRANGE) {
230 Com_Printf("%.2f ", aircraft->stats[k] / 1000.0f);
231 } else {
232 Com_Printf("%i ", aircraft->stats[k]);
233 }
234 }
235 Com_Printf("\n");
236 Com_Printf("...name %s\n", aircraft->id);
237 Com_Printf("...type %i\n", aircraft->type);
238 Com_Printf("...size %i\n", aircraft->maxTeamSize);
239 Com_Printf("...fuel %i\n", aircraft->fuel);
240 Com_Printf("...status %s\n", (aircraft->status == AIR_CRASHED) ? "crashed" : AIR_AircraftStatusToName(aircraft));
241 Com_Printf("...pos %.0f:%.0f\n", aircraft->pos[0], aircraft->pos[1]);
242 Com_Printf("...team: (%i/%i)\n", LIST_Count(aircraft->acTeam), aircraft->maxTeamSize);
243 LIST_Foreach(aircraft->acTeam, employee_t, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (aircraft->acTeam); ! employee__break && employee__iter
;) for (employee_t* const employee = ( employee__break = employee__once
= true, (employee_t*) employee__iter->data); employee__once
; employee__break = employee__once = false) if ( employee__iter
= employee__iter->next, false) {} else
{
244 character_t *chr = &employee->chr;
245 Com_Printf(".........name: %s (ucn: %i)\n", chr->name, chr->ucn);
246 }
247 }
248}
249#endif
250
251static equipDef_t eTempEq; /**< Used to count ammo in magazines. */
252
253/**
254 * @brief Count and collect ammo from gun magazine.
255 * @param[in] magazine Pointer to invList_t being magazine.
256 * @param[in] aircraft Pointer to aircraft used in this mission.
257 * @sa AII_CollectingItems
258 */
259static void AII_CollectingAmmo (void *data, const invList_t *magazine)
260{
261 aircraft_t *aircraft = (aircraft_t *)data;
262 /* Let's add remaining ammo to market. */
263 eTempEq.numItemsLoose[magazine->item.m->idx] += magazine->item.a;
264 if (eTempEq.numItemsLoose[magazine->item.m->idx] >= magazine->item.t->ammo) {
265 /* There are more or equal ammo on the market than magazine needs - collect magazine. */
266 eTempEq.numItemsLoose[magazine->item.m->idx] -= magazine->item.t->ammo;
267 AII_CollectItem(aircraft, magazine->item.m, 1);
268 }
269}
270
271/**
272 * @brief Add an item to aircraft inventory.
273 * @param[in,out] aircraft Aircraft to load to
274 * @param[in] item Item to add
275 * @param amount Number of items to add
276 * @sa AL_AddAliens
277 * @sa AII_CollectingItems
278 */
279void AII_CollectItem (aircraft_t *aircraft, const objDef_t *item, int amount)
280{
281 int i;
282 itemsTmp_t *cargo = aircraft->itemcargo;
283
284 for (i = 0; i < aircraft->itemTypes; i++) {
285 if (cargo[i].item == item) {
286 Com_DPrintf(DEBUG_CLIENT0x20, "AII_CollectItem: collecting %s (%i) amount %i -> %i\n", item->name, item->idx, cargo[i].amount, cargo[i].amount + amount);
287 cargo[i].amount += amount;
288 return;
289 }
290 }
291
292 if (aircraft->itemTypes >= MAX_CARGO32) {
293 Com_Printf("AII_CollectItem: Cannot add item to cargobay it's full!\n");
294 return;
295 }
296
297 Com_DPrintf(DEBUG_CLIENT0x20, "AII_CollectItem: adding %s (%i) amount %i\n", item->name, item->idx, amount);
298 cargo[aircraft->itemTypes].item = item;
299 cargo[aircraft->itemTypes].amount = amount;
300 aircraft->itemTypes++;
301}
302
303static inline void AII_CollectItem_ (void *data, const objDef_t *item, int amount)
304{
305 AII_CollectItem((aircraft_t *)data, item, amount);
306}
307
308/**
309 * @brief Process items carried by soldiers.
310 * @param[in] soldierInventory Pointer to inventory from a soldier of our team.
311 * @sa AII_CollectingItems
312 */
313static void AII_CarriedItems (const inventory_t *soldierInventory)
314{
315 containerIndex_t container;
316 invList_t *invList;
317 equipDef_t *ed = &ccs.eMission;
318
319 for (container = 0; container < csi.numIDs; container++) {
320 /* Items on the ground are collected as ET_ITEM */
321 if (INVDEF(container)(&csi.ids[(container)])->temp)
322 continue;
323 for (invList = soldierInventory->c[container]; invList; invList = invList->next) {
324 const objDef_t *item = invList->item.t;
325 const objDef_t *ammo = invList->item.m;
326 technology_t *tech = RS_GetTechForItem(item);
327 ed->numItems[item->idx]++;
328 RS_MarkCollected(tech);
329
330 if (!item->reload || invList->item.a == 0)
331 continue;
332
333 ed->numItemsLoose[ammo->idx] += invList->item.a;
334 if (ed->numItemsLoose[ammo->idx] >= item->ammo) {
335 ed->numItemsLoose[ammo->idx] -= item->ammo;
336 ed->numItems[ammo->idx]++;
337 }
338 /* The guys keep their weapons (half-)loaded. Auto-reload
339 * will happen at equip screen or at the start of next mission,
340 * but fully loaded weapons will not be reloaded even then. */
341 }
342 }
343}
344
345/**
346 * @brief Collect items from the battlefield.
347 * @note The way we are collecting items:
348 * @note 1. Grab everything from the floor and add it to the aircraft cargo here.
349 * @note 2. When collecting gun, collect it's remaining ammo as well
350 * @note 3. Sell everything or add to base storage when dropship lands in base.
351 * @note 4. Items carried by soldiers are processed nothing is being sold.
352 * @sa CL_ParseResults
353 * @sa AII_CollectingAmmo
354 * @sa AII_CarriedItems
355 */
356void AII_CollectingItems (aircraft_t *aircraft, int won)
357{
358 int i, j;
359 itemsTmp_t *cargo;
360 itemsTmp_t prevItemCargo[MAX_CARGO32];
361 int prevItemTypes;
362
363 /* Save previous cargo */
364 memcpy(prevItemCargo, aircraft->itemcargo, sizeof(aircraft->itemcargo));
365 prevItemTypes = aircraft->itemTypes;
366 /* Make sure itemcargo is empty. */
367 OBJZERO(aircraft->itemcargo)(memset(&((aircraft->itemcargo)), (0), sizeof((aircraft
->itemcargo))))
;
368
369 /* Make sure eTempEq is empty as well. */
370 OBJZERO(eTempEq)(memset(&((eTempEq)), (0), sizeof((eTempEq))));
371
372 cargo = aircraft->itemcargo;
373 aircraft->itemTypes = 0;
374
375 cgi->CollectItems(aircraft, won, AII_CollectItem_, AII_CollectingAmmo, AII_CarriedItems);
376
377 /* Fill the missionResults array. */
378 ccs.missionResults.itemTypes = aircraft->itemTypes;
379 for (i = 0; i < aircraft->itemTypes; i++)
380 ccs.missionResults.itemAmount += cargo[i].amount;
381
382#ifdef DEBUG1
383 /* Print all of collected items. */
384 for (i = 0; i < aircraft->itemTypes; i++) {
385 if (cargo[i].amount > 0)
386 Com_DPrintf(DEBUG_CLIENT0x20, "Collected items: idx: %i name: %s amount: %i\n", cargo[i].item->idx, cargo[i].item->name, cargo[i].amount);
387 }
388#endif
389
390 /* Put previous cargo back */
391 for (i = 0; i < prevItemTypes; i++) {
392 for (j = 0; j < aircraft->itemTypes; j++) {
393 if (cargo[j].item == prevItemCargo[i].item) {
394 cargo[j].amount += prevItemCargo[i].amount;
395 break;
396 }
397 }
398 if (j == aircraft->itemTypes) {
399 cargo[j] = prevItemCargo[i];
400 aircraft->itemTypes++;
401 }
402 }
403}
404
405/**
406 * @brief Translates the aircraft status id to a translatable string
407 * @param[in] aircraft Aircraft to translate the status of
408 * @return Translation string of given status.
409 */
410const char *AIR_AircraftStatusToName (const aircraft_t * aircraft)
411{
412 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 412, "aircraft") : (void)0)
;
413 assert(aircraft->homebase)(__builtin_expect(!(aircraft->homebase), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_aircraft.cpp", 413, "aircraft->homebase"
) : (void)0)
;
414
415 /* display special status if base-attack affects aircraft */
416 if (B_IsUnderAttack(aircraft->homebase)((aircraft->homebase)->baseStatus == BASE_UNDER_ATTACK) && AIR_IsAircraftInBase(aircraft))
417 return _("ON RED ALERT")gettext("ON RED ALERT");
418
419 switch (aircraft->status) {
420 case AIR_NONE:
421 return _("Nothing - should not be displayed")gettext("Nothing - should not be displayed");
422 case AIR_HOME:
423 return _("at home base")gettext("at home base");
424 case AIR_REFUEL:
425 return _("refuelling")gettext("refuelling");
426 case AIR_IDLE:
427 return _("idle")gettext("idle");
428 case AIR_TRANSIT:
429 return _("in transit")gettext("in transit");
430 case AIR_MISSION:
431 return _("enroute to mission")gettext("enroute to mission");
432 case AIR_UFO:
433 return _("pursuing a UFO")gettext("pursuing a UFO");
434 case AIR_DROP:
435 return _("ready to drop soldiers")gettext("ready to drop soldiers");
436 case AIR_INTERCEPT:
437 return _("intercepting a UFO")gettext("intercepting a UFO");
438 case AIR_TRANSFER:
439 return _("enroute to new home base")gettext("enroute to new home base");
440 case AIR_RETURNING:
441 return _("returning to base")gettext("returning to base");
442 case AIR_CRASHED:
443 Com_Error(ERR_DROP1, "AIR_CRASHED should not be visible anywhere");
444 }
445 return NULL__null;
446}
447
448/**
449 * @brief Checks whether given aircraft is in its homebase.
450 * @param[in] aircraft Pointer to an aircraft.
451 * @return true if given aircraft is in its homebase.
452 * @return false if given aircraft is not in its homebase.
453 */
454bool AIR_IsAircraftInBase (const aircraft_t * aircraft)
455{
456 if (aircraft->status == AIR_HOME || aircraft->status == AIR_REFUEL)
457 return true;
458 return false;
459}
460
461/**
462 * @brief Checks whether given aircraft is on geoscape.
463 * @param[in] aircraft Pointer to an aircraft.
464 * @note aircraft may be neither on geoscape, nor in base (like when it's transferred)
465 * @return @c false if given aircraft is not on geoscape, @c true if given aircraft is on geoscape.
466 * @sa UFO_IsUFOSeenOnGeoscape
467 */
468bool AIR_IsAircraftOnGeoscape (const aircraft_t *aircraft)
469{
470 switch (aircraft->status) {
471 case AIR_IDLE:
472 case AIR_TRANSIT:
473 case AIR_MISSION:
474 case AIR_UFO:
475 case AIR_DROP:
476 case AIR_INTERCEPT:
477 case AIR_RETURNING:
478 return true;
479 case AIR_NONE:
480 case AIR_REFUEL:
481 case AIR_HOME:
482 case AIR_TRANSFER:
483 case AIR_CRASHED:
484 return false;
485 }
486
487 Com_Error(ERR_FATAL0, "Unknown aircraft status %i", aircraft->status);
488}
489
490/**
491 * @brief Calculates the amount of aircraft (of the given type) in the selected base
492 * @param[in] base The base to count the aircraft in
493 * @param[in] aircraftType The type of the aircraft you want
494 * @note that this type is just transporter/interceptor/ufo category
495 */
496int AIR_CountTypeInBase (const base_t *base, aircraftType_t aircraftType)
497{
498 int count = 0;
499
500 AIR_ForeachFromBase(aircraft, base)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((base)) && (aircraft)->status != AIR_CRASHED
)) continue; else
{
501 if (aircraft->type == aircraftType)
502 count++;
503 }
504 return count;
505}
506
507/**
508 * @brief Calculates the amount of aircraft (of the given type) in the selected base
509 * @param[in] base The base to count the aircraft in
510 * @param[in] aircraftTemplate The type of the aircraft you want
511 */
512int AIR_CountInBaseByTemplate (const base_t *base, const aircraft_t *aircraftTemplate)
513{
514 int count = 0;
515
516 AIR_ForeachFromBase(aircraft, base)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((base)) && (aircraft)->status != AIR_CRASHED
)) continue; else
{
517 if (aircraft->tpl == aircraftTemplate)
518 count++;
519 }
520 return count;
521}
522
523/**
524 * @brief Returns the string that matches the given aircraft type
525 */
526const char *AIR_GetAircraftString (aircraftType_t aircraftType)
527{
528 switch (aircraftType) {
529 case AIRCRAFT_INTERCEPTOR:
530 return _("Interceptor")gettext("Interceptor");
531 case AIRCRAFT_TRANSPORTER:
532 return _("Transporter")gettext("Transporter");
533 case AIRCRAFT_UFO:
534 return _("UFO")gettext("UFO");
535 }
536 return "";
537}
538
539/**
540 * @brief Some of the aircraft values needs special calculations when they
541 * are shown in the menus
542 * @sa UP_AircraftStatToName
543 */
544int AIR_AircraftMenuStatsValues (const int value, const int stat)
545{
546 switch (stat) {
547 case AIR_STATS_SPEED:
548 case AIR_STATS_MAXSPEED:
549 /* Convert into km/h, and round this value */
550 return 10 * (int) (111.2 * value / 10.0f);
551 case AIR_STATS_FUELSIZE:
552 return value / 1000;
553 default:
554 return value;
555 }
556}
557
558/**
559 * @brief Calculates the range an aircraft can fly on the geoscape
560 * @param aircraft The aircraft to calculate the range for
561 * @return The range
562 */
563int AIR_GetOperationRange (const aircraft_t *aircraft)
564{
565 const int range = aircraft->stats[AIR_STATS_SPEED] * aircraft->stats[AIR_STATS_FUELSIZE];
566 /* the 2.0f factor is for going to destination and then come back */
567 return 100 * (int) (KILOMETER_PER_DEGREE111.2 * range / (2.0f * (float)SECONDS_PER_HOUR3600 * 100.0f));
568}
569
570/**
571 * @brief Calculates the remaining range the aircraft can fly
572 * @param aircraft The aircraft to calculate the remaining range for
573 * @return The remaining range
574 */
575int AIR_GetRemainingRange (const aircraft_t *aircraft)
576{
577 return aircraft->stats[AIR_STATS_SPEED] * aircraft->fuel;
578}
579
580/**
581 * @brief check if aircraft has enough fuel to go to destination, and then come back home
582 * @param[in] aircraft Pointer to the aircraft
583 * @param[in] destination Pointer to the position the aircraft should go to
584 * @sa MAP_MapCalcLine
585 * @return true if the aircraft can go to the position, false else
586 */
587bool AIR_AircraftHasEnoughFuel (const aircraft_t *aircraft, const vec2_t destination)
588{
589 const base_t *base;
590 float distance;
591
592 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 592, "aircraft") : (void)0)
;
593 base = aircraft->homebase;
594 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 594, "base") : (void)0)
;
595
596 /* Calculate the line that the aircraft should follow to go to destination */
597 distance = GetDistanceOnGlobe(aircraft->pos, destination);
598
599 /* Calculate the line that the aircraft should then follow to go back home */
600 distance += GetDistanceOnGlobe(destination, base->pos);
601
602 /* Check if the aircraft has enough fuel to go to destination and then go back home */
603 return (distance <= AIR_GetRemainingRange(aircraft) / (float)SECONDS_PER_HOUR3600);
604}
605
606/**
607 * @brief check if aircraft has enough fuel to go to destination
608 * @param[in] aircraft Pointer to the aircraft
609 * @param[in] destination Pointer to the position the aircraft should go to
610 * @sa MAP_MapCalcLine
611 * @return true if the aircraft can go to the position, false else
612 */
613bool AIR_AircraftHasEnoughFuelOneWay (const aircraft_t *aircraft, const vec2_t destination)
614{
615 float distance;
616
617 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 617, "aircraft") : (void)0)
;
618
619 /* Calculate the line that the aircraft should follow to go to destination */
620 distance = GetDistanceOnGlobe(aircraft->pos, destination);
621
622 /* Check if the aircraft has enough fuel to go to destination */
623 return (distance <= AIR_GetRemainingRange(aircraft) / (float)SECONDS_PER_HOUR3600);
624}
625
626/**
627 * @brief Calculates the way back to homebase for given aircraft and returns it.
628 * @param[in] aircraft Pointer to aircraft, which should return to base.
629 * @note Command to call this: "aircraft_return".
630 */
631void AIR_AircraftReturnToBase (aircraft_t *aircraft)
632{
633 if (aircraft && AIR_IsAircraftOnGeoscape(aircraft)) {
634 const base_t *base = aircraft->homebase;
635 MAP_MapCalcLine(aircraft->pos, base->pos, &aircraft->route);
636 aircraft->status = AIR_RETURNING;
637 aircraft->time = 0;
638 aircraft->point = 0;
639 aircraft->mission = NULL__null;
640 aircraft->aircraftTarget = NULL__null;
641 }
642}
643
644/**
645 * @brief Returns the index of the aircraft in the base->aircraft array.
646 * @param[in] aircraft The aircraft to get the index for.
647 * @return The array index or AIRCRAFT_INBASE_INVALID on error.
648 * @todo Remove this! Aircraft no longer have local index per base
649 */
650int AIR_GetAircraftIDXInBase (const aircraft_t* aircraft)
651{
652 int i;
653 const base_t *base;
654
655 if (!aircraft || !aircraft->homebase)
656 return AIRCRAFT_INBASE_INVALID-1;
657
658 base = aircraft->homebase;
659
660 i = 0;
661 AIR_ForeachFromBase(aircraftInBase, base)for (bool aircraftInBase__break = false, aircraftInBase__once
= true; aircraftInBase__once; aircraftInBase__once = false) for
(linkedList_t const* aircraftInBase__iter = (ccs.aircraft); !
aircraftInBase__break && aircraftInBase__iter;) for (
aircraft_t* const aircraftInBase = ( aircraftInBase__break = aircraftInBase__once
= true, (aircraft_t*) aircraftInBase__iter->data); aircraftInBase__once
; aircraftInBase__break = aircraftInBase__once = false) if ( aircraftInBase__iter
= aircraftInBase__iter->next, false) {} else if (!((aircraftInBase
)->homebase == ((base)) && (aircraftInBase)->status
!= AIR_CRASHED)) continue; else
{
662 if (aircraftInBase == aircraft)
663 return i;
664 i++;
665 }
666
667 /* Aircraft not found in base */
668 return AIRCRAFT_INBASE_INVALID-1;
669}
670
671/**
672 * @param base The base to get the aircraft from
673 * @param index The index of the aircraft in the given base
674 * @return @c NULL if there is no such aircraft in the given base, or the aircraft pointer that belongs to the given index.
675 * @todo Remove this! Aircraft no longer have local index per base
676 */
677aircraft_t *AIR_GetAircraftFromBaseByIDXSafe (const base_t* base, int index)
678{
679 int i;
680
681 i = 0;
682 AIR_ForeachFromBase(aircraft, base)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((base)) && (aircraft)->status != AIR_CRASHED
)) continue; else
{
683 if (index == i)
684 return aircraft;
685 i++;
686 }
687
688 return NULL__null;
689}
690
691/**
692 * @brief Searches the global array of aircraft types for a given aircraft.
693 * @param[in] name Aircraft id.
694 * @return aircraft_t pointer or NULL if not found.
695 * @note This function gives no warning on null name or if no aircraft found
696 */
697const aircraft_t *AIR_GetAircraftSilent (const char *name)
698{
699 int i;
700
701 if (!name)
702 return NULL__null;
703 for (i = 0; i < ccs.numAircraftTemplates; i++) {
704 const aircraft_t *aircraftTemplate = &ccs.aircraftTemplates[i];
705 if (Q_streq(aircraftTemplate->id, name)(strcmp(aircraftTemplate->id, name) == 0))
706 return aircraftTemplate;
707 }
708 return NULL__null;
709}
710
711/**
712 * @brief Searches the global array of aircraft types for a given aircraft.
713 * @param[in] name Aircraft id.
714 * @return aircraft_t pointer or errors out (ERR_DROP)
715 */
716const aircraft_t *AIR_GetAircraft (const char *name)
717{
718 const aircraft_t *aircraft = AIR_GetAircraftSilent(name);
719 if (name == NULL__null || name[0] == '\0')
720 Com_Error(ERR_DROP1, "AIR_GetAircraft called with NULL name!");
721 else if (aircraft == NULL__null)
722 Com_Error(ERR_DROP1, "Aircraft '%s' not found", name);
723
724 return aircraft;
725}
726
727/**
728 * @brief Initialise aircraft pointer in each slot of an aircraft.
729 * @param[in] aircraft Pointer to the aircraft where slots are.
730 */
731static void AII_SetAircraftInSlots (aircraft_t *aircraft)
732{
733 int i;
734
735 /* initialise weapon slots */
736 for (i = 0; i < MAX_AIRCRAFTSLOT4; i++) {
737 aircraft->weapons[i].aircraft = aircraft;
738 aircraft->electronics[i].aircraft = aircraft;
739 }
740 aircraft->shield.aircraft = aircraft;
741}
742
743/**
744 * @brief Adds a new aircraft from a given aircraft template to the base and sets the homebase for the new aircraft
745 * to the given base
746 * @param[out] base The base to add the aircraft to
747 * @param[in] aircraftTemplate The template to create the new aircraft from
748 * @return the newly added aircraft
749 * @sa AIR_Delete
750 */
751aircraft_t *AIR_Add (base_t *base, const aircraft_t *aircraftTemplate)
752{
753 aircraft_t& aircraft = LIST_Add(&ccs.aircraft, *aircraftTemplate);
754 aircraft.homebase = base;
755 return &aircraft;
756}
757
758/**
759 * @brief Will remove the given aircraft from the base
760 * @param[out] base The base to remove the aircraft from
761 * @param aircraft The aircraft to remove
762 * @return @c true if the aircraft was removed, @c false otherwise
763 * @sa AIR_Add
764 */
765bool AIR_Delete (base_t *base, const aircraft_t *aircraft)
766{
767 return LIST_Remove(&ccs.aircraft, (const void *)aircraft);
768}
769
770/**
771 * @brief Places a new aircraft in the given base.
772 * @param[in] base Pointer to base where aircraft should be added.
773 * @param[in] aircraftTemplate The aircraft template to create the new aircraft from.
774 * @sa B_Load
775 */
776aircraft_t* AIR_NewAircraft (base_t *base, const aircraft_t *aircraftTemplate)
777{
778 /* copy generic aircraft description to individual aircraft in base
779 * we do this because every aircraft can have its own parameters
780 * now lets use the aircraft array for the base to set some parameters */
781 aircraft_t *aircraft = AIR_Add(base, aircraftTemplate);
782 aircraft->idx = ccs.campaignStats.aircraftHad++; /**< set a unique index to this aircraft. */
783 aircraft->homebase = base;
784 /* Update the values of its stats */
785 AII_UpdateAircraftStats(aircraft);
786 /* initialise aircraft pointer in slots */
787 AII_SetAircraftInSlots(aircraft);
788 /* give him some fuel */
789 aircraft->fuel = aircraft->stats[AIR_STATS_FUELSIZE];
790 /* Set HP to maximum */
791 aircraft->damage = aircraft->stats[AIR_STATS_DAMAGE];
792 /* Set Default Name */
793 Q_strncpyz(aircraft->name, _(aircraft->defaultName), lengthof(aircraft->name))Q_strncpyzDebug( aircraft->name, gettext(aircraft->defaultName
), (sizeof(aircraft->name) / sizeof(*(aircraft->name)))
, "src/client/cgame/campaign/cp_aircraft.cpp", 793 )
;
794
795 /* set initial direction of the aircraft */
796 VectorSet(aircraft->direction, 1, 0, 0)((aircraft->direction)[0]=(1), (aircraft->direction)[1]
=(0), (aircraft->direction)[2]=(0))
;
797
798 AIR_ResetAircraftTeam(aircraft);
799
800 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new %s is ready in %s")gettext("A new %s is ready in %s"), _(aircraft->tpl->name)gettext(aircraft->tpl->name), base->name);
801 MS_AddNewMessage(_("Notice")gettext("Notice"), cp_messageBuffer);
802 Com_DPrintf(DEBUG_CLIENT0x20, "Setting aircraft to pos: %.0f:%.0f\n", base->pos[0], base->pos[1]);
803 Vector2Copy(base->pos, aircraft->pos)((aircraft->pos)[0]=(base->pos)[0],(aircraft->pos)[1
]=(base->pos)[1])
;
804 RADAR_Initialise(&aircraft->radar, RADAR_AIRCRAFTRANGE, RADAR_AIRCRAFTTRACKINGRANGE, 1.0f, false);
805 aircraft->radar.ufoDetectionProbability = 1;
806
807 /* Update base capacities. */
808 Com_DPrintf(DEBUG_CLIENT0x20, "idx_sample: %i name: %s weight: %i\n", aircraft->tpl->idx, aircraft->id, aircraft->size);
809 Com_DPrintf(DEBUG_CLIENT0x20, "Adding new aircraft %s with IDX %i for %s\n", aircraft->id, aircraft->idx, base->name);
810 if (!base->aircraftCurrent)
811 base->aircraftCurrent = aircraft;
812 aircraft->hangar = AIR_UpdateHangarCapForOne(aircraft->tpl, base);
813 if (aircraft->hangar == AIRCRAFT_HANGAR_ERROR)
814 Com_Printf("AIR_NewAircraft: ERROR, new aircraft but no free space in hangars!\n");
815 /* also update the base menu buttons */
816 Cmd_ExecuteString("base_init");
817 return aircraft;
818}
819
820/**
821 * @brief Returns capacity type needed for an aircraft
822 * @param[in] aircraft Aircraft to check
823 */
824baseCapacities_t AIR_GetCapacityByAircraftWeight (const aircraft_t *aircraft)
825{
826 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 826, "aircraft") : (void)0)
;
827 switch (aircraft->size) {
828 case AIRCRAFT_SMALL:
829 return CAP_AIRCRAFT_SMALL;
830 case AIRCRAFT_LARGE:
831 return CAP_AIRCRAFT_BIG;
832 }
833 Com_Error(ERR_DROP1, "AIR_GetCapacityByAircraftWeight: Unknown weight of aircraft '%i'\n", aircraft->size);
834}
835
836/**
837 * @brief Calculate used storage room corresponding to items in an aircraft.
838 * @param[in] aircraft Pointer to the aircraft.
839 */
840static int AIR_GetStorageRoom (const aircraft_t *aircraft)
841{
842 int size = 0;
843
844 LIST_Foreach(aircraft->acTeam, employee_t, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (aircraft->acTeam); ! employee__break && employee__iter
;) for (employee_t* const employee = ( employee__break = employee__once
= true, (employee_t*) employee__iter->data); employee__once
; employee__break = employee__once = false) if ( employee__iter
= employee__iter->next, false) {} else
{
845 containerIndex_t container;
846 for (container = 0; container < csi.numIDs; container++) {
847 invList_t *ic;
848#if 0
849 /* ignore items linked from any temp container */
850 if (INVDEF(container)(&csi.ids[(container)])->temp)
851 continue;
852#endif
853 for (ic = CONTAINER(&employee->chr, container)((&employee->chr)->i.c[(container)]); ic; ic = ic->next) {
854 const objDef_t *obj = ic->item.t;
855 size += obj->size;
856
857 obj = ic->item.m;
858 if (obj)
859 size += obj->size;
860 }
861 }
862 }
863
864 return size;
865}
866
867const char *AIR_CheckMoveIntoNewHomebase (const aircraft_t *aircraft, const base_t* base, const baseCapacities_t capacity)
868{
869 if (!B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(capacity)))
870 return _("No operational hangars at that base.")gettext("No operational hangars at that base.");
871
872 /* not enough capacity */
873 if (CAP_GetFreeCapacity(base, capacity) <= 0)
874 return _("No free hangars at that base.")gettext("No free hangars at that base.");
875
876 if (CAP_GetFreeCapacity(base, CAP_EMPLOYEES) < AIR_GetTeamSize(aircraft) + (AIR_GetPilot(aircraft) ? 1 : 0))
877 return _("Insufficient free crew quarter space at that base.")gettext("Insufficient free crew quarter space at that base.");
878
879 if (aircraft->maxTeamSize && CAP_GetFreeCapacity(base, CAP_ITEMS) < AIR_GetStorageRoom(aircraft))
880 return _("Insufficient storage space at that base.")gettext("Insufficient storage space at that base.");
881
882 /* check aircraft fuel, because the aircraft has to travel to the new base */
883 if (!AIR_AircraftHasEnoughFuelOneWay(aircraft, base->pos))
884 return _("That base is beyond this aircraft's range.")gettext("That base is beyond this aircraft's range.");
885
886 return NULL__null;
887}
888
889/**
890 * @brief Transfer items carried by a soldier from one base to another.
891 * @param[in] chr Pointer to the character.
892 * @param[in] sourceBase Base where employee comes from.
893 * @param[in] destBase Base where employee is going.
894 */
895static void AIR_TransferItemsCarriedByCharacterToBase (character_t *chr, base_t *sourceBase, base_t* destBase)
896{
897 const invList_t *ic;
898 containerIndex_t container;
899
900 for (container = 0; container < csi.numIDs; container++) {
901#if 0
902 /* ignore items linked from any temp container */
903 if (INVDEF(container)(&csi.ids[(container)])->temp)
904 continue;
905#endif
906 for (ic = CONTAINER(chr, container)((chr)->i.c[(container)]); ic; ic = ic->next) {
907 const objDef_t *obj = ic->item.t;
908 B_UpdateStorageAndCapacity(sourceBase, obj, -1, false);
909 B_UpdateStorageAndCapacity(destBase, obj, 1, false);
910
911 obj = ic->item.m;
912 if (obj) {
913 B_UpdateStorageAndCapacity(sourceBase, obj, -1, false);
914 B_UpdateStorageAndCapacity(destBase, obj, 1, false);
915 }
916 }
917 }
918}
919
920/**
921 * @brief Moves a given aircraft to a new base (also the employees and inventory)
922 * @note Also checks for a working hangar
923 * @param[in] aircraft The aircraft to move into a new base
924 * @param[in] base The base to move the aircraft into
925 */
926bool AIR_MoveAircraftIntoNewHomebase (aircraft_t *aircraft, base_t *base)
927{
928 base_t *oldBase;
929 baseCapacities_t capacity;
930
931 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 931, "aircraft") : (void)0)
;
932 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 932, "base") : (void)0)
;
933 assert(base != aircraft->homebase)(__builtin_expect(!(base != aircraft->homebase), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_aircraft.cpp", 933, "base != aircraft->homebase"
) : (void)0)
;
934
935 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_MoveAircraftIntoNewHomebase: Change homebase of '%s' to '%s'\n", aircraft->id, base->name);
936
937 /* Is aircraft being transfered? */
938 if (aircraft->status == AIR_TRANSFER) {
939 /* Move the aircraft to the new base to avoid fuel problems */
940 VectorCopy(base->pos, aircraft->pos)((aircraft->pos)[0]=(base->pos)[0],(aircraft->pos)[1
]=(base->pos)[1],(aircraft->pos)[2]=(base->pos)[2])
;
941 aircraft->status = AIR_HOME;
942 }
943
944 capacity = AIR_GetCapacityByAircraftWeight(aircraft);
945 if (AIR_CheckMoveIntoNewHomebase(aircraft, base, capacity))
946 return false;
947
948 oldBase = aircraft->homebase;
949 assert(oldBase)(__builtin_expect(!(oldBase), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 949, "oldBase") : (void)0)
;
950
951 /* Transfer employees */
952 E_MoveIntoNewBase(AIR_GetPilot(aircraft), base);
953
954 LIST_Foreach(aircraft->acTeam, employee_t, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (aircraft->acTeam); ! employee__break && employee__iter
;) for (employee_t* const employee = ( employee__break = employee__once
= true, (employee_t*) employee__iter->data); employee__once
; employee__break = employee__once = false) if ( employee__iter
= employee__iter->next, false) {} else
{
955 E_MoveIntoNewBase(employee, base);
956 /* Transfer items carried by soldiers from oldBase to base */
957 AIR_TransferItemsCarriedByCharacterToBase(&employee->chr, oldBase, base);
958 }
959
960 /* Move aircraft to new base */
961 CAP_AddCurrent(oldBase, capacity, -1);
962 aircraft->homebase = base;
963 CAP_AddCurrent(base, capacity, 1);
964
965 if (oldBase->aircraftCurrent == aircraft)
966 oldBase->aircraftCurrent = AIR_GetFirstFromBase(oldBase);
967 if (!base->aircraftCurrent)
968 base->aircraftCurrent = aircraft;
969
970 /* No need to update global IDX of every aircraft: the global IDX of this aircraft did not change */
971 /* Redirect selectedAircraft */
972 MAP_SelectAircraft(aircraft);
973
974 if (aircraft->status == AIR_RETURNING) {
975 /* redirect to the new base */
976 AIR_AircraftReturnToBase(aircraft);
977 }
978
979 return true;
980}
981
982/**
983 * @brief Removes an aircraft from its base and the game.
984 * @param[in] aircraft Pointer to aircraft that should be removed.
985 * @note The assigned soldiers (if any) are removed/unassinged from the aircraft - not fired.
986 * @sa AIR_DestroyAircraft
987 * @note If you want to do something different (kill, fire, etc...) do it before calling this function.
988 * @todo Return status of deletion for better error handling.
989 */
990void AIR_DeleteAircraft (aircraft_t *aircraft)
991{
992 int i;
993 base_t *base;
994 /* Check if aircraft is on geoscape while it's not destroyed yet */
995 const bool aircraftIsOnGeoscape = AIR_IsAircraftOnGeoscape(aircraft);
996
997 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 997, "aircraft") : (void)0)
;
998 base = aircraft->homebase;
999 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 999, "base") : (void)0)
;
1000
1001 MAP_NotifyAircraftRemoved(aircraft);
1002 TR_NotifyAircraftRemoved(aircraft);
1003
1004 /* Remove pilot and all soldiers from the aircraft (the employees are still hired after this). */
1005 AIR_RemoveEmployees(aircraft);
1006
1007 /* base is NULL here because we don't want to readd this to the inventory
1008 * If you want this in the inventory you really have to call these functions
1009 * before you are destroying a craft */
1010 for (i = 0; i < MAX_AIRCRAFTSLOT4; i++) {
1011 AII_RemoveItemFromSlot(NULL__null, aircraft->weapons, false);
1012 AII_RemoveItemFromSlot(NULL__null, aircraft->electronics, false);
1013 }
1014 AII_RemoveItemFromSlot(NULL__null, &aircraft->shield, false);
1015
1016 if (base->aircraftCurrent == aircraft)
1017 base->aircraftCurrent = NULL__null;
1018
1019 AIR_Delete(base, aircraft);
1020
1021 if (!AIR_BaseHasAircraft(base)) {
1022 Cvar_SetValue("mn_equipsoldierstate", 0);
1023 Cvar_Set("mn_aircraftstatus", "");
1024 Cvar_Set("mn_aircraftinbase", "0");
1025 Cvar_Set("mn_aircraftname", "");
1026 Cvar_Set("mn_aircraft_model", "");
1027 } else if (base->aircraftCurrent == NULL__null) {
1028 base->aircraftCurrent = AIR_GetFirstFromBase(base);
1029 }
1030
1031 /* also update the base menu buttons */
1032 Cmd_ExecuteString("base_init");
1033
1034 /* update hangar capacities */
1035 AIR_UpdateHangarCapForAll(base);
1036
1037 /* Update Radar overlay */
1038 if (aircraftIsOnGeoscape)
1039 RADAR_UpdateWholeRadarOverlay();
1040}
1041
1042/**
1043 * @brief Removes an aircraft from its base and the game.
1044 * @param[in] aircraft Pointer to aircraft that should be removed.
1045 * @note aircraft and assigned soldiers (if any) are removed from game.
1046 * @todo Return status of deletion for better error handling.
1047 */
1048void AIR_DestroyAircraft (aircraft_t *aircraft)
1049{
1050 employee_t *pilot;
1051
1052 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 1052, "aircraft") : (void)0)
;
1053
1054 LIST_Foreach(aircraft->acTeam, employee_t, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (aircraft->acTeam); ! employee__break && employee__iter
;) for (employee_t* const employee = ( employee__break = employee__once
= true, (employee_t*) employee__iter->data); employee__once
; employee__break = employee__once = false) if ( employee__iter
= employee__iter->next, false) {} else
{
1055 E_RemoveInventoryFromStorage(employee);
1056 E_DeleteEmployee(employee);
1057 }
1058 /* the craft may no longer have any employees assigned */
1059 /* remove the pilot */
1060 pilot = AIR_GetPilot(aircraft);
1061 if (pilot) {
1062 if (E_DeleteEmployee(pilot))
1063 AIR_SetPilot(aircraft, NULL__null);
1064 else
1065 Com_Error(ERR_DROP1, "AIR_DestroyAircraft: Could not remove pilot from game: %s (ucn: %i)\n", pilot->chr.name, pilot->chr.ucn);
1066 } else {
1067 if (aircraft->status != AIR_CRASHED)
1068 Com_Error(ERR_DROP1, "AIR_DestroyAircraft: aircraft id %s had no pilot\n", aircraft->id);
1069 }
1070
1071 AIR_DeleteAircraft(aircraft);
1072}
1073
1074/**
1075 * @brief Moves given aircraft.
1076 * @param[in] dt time delta
1077 * @param[in] aircraft Pointer to aircraft on its way.
1078 * @return true if the aircraft reached its destination.
1079 */
1080bool AIR_AircraftMakeMove (int dt, aircraft_t* aircraft)
1081{
1082 float dist;
1083
1084 /* calc distance */
1085 aircraft->time += dt;
1086 aircraft->fuel -= dt;
1087
1088 dist = (float) aircraft->stats[AIR_STATS_SPEED] * aircraft->time / (float)SECONDS_PER_HOUR3600;
1089
1090 /* Check if destination reached */
1091 if (dist >= aircraft->route.distance * (aircraft->route.numPoints - 1)) {
1092 return true;
1093 } else {
1094 /* calc new position */
1095 float frac = dist / aircraft->route.distance;
1096 const int p = (int) frac;
1097 frac -= p;
1098 aircraft->point = p;
1099 aircraft->pos[0] = (1 - frac) * aircraft->route.point[p][0] + frac * aircraft->route.point[p + 1][0];
1100 aircraft->pos[1] = (1 - frac) * aircraft->route.point[p][1] + frac * aircraft->route.point[p + 1][1];
1101
1102 MAP_CheckPositionBoundaries(aircraft->pos);
1103 }
1104
1105 dist = (float) aircraft->stats[AIR_STATS_SPEED] * (aircraft->time + dt) / (float)SECONDS_PER_HOUR3600;
1106
1107 /* Now calculate the projected position. This is the position that the aircraft should get on
1108 * next frame if its route didn't change in meantime. */
1109 if (dist >= aircraft->route.distance * (aircraft->route.numPoints - 1)) {
1110 VectorSet(aircraft->projectedPos, 0.0f, 0.0f, 0.0f)((aircraft->projectedPos)[0]=(0.0f), (aircraft->projectedPos
)[1]=(0.0f), (aircraft->projectedPos)[2]=(0.0f))
;
1111 } else {
1112 /* calc new position */
1113 float frac = dist / aircraft->route.distance;
1114 const int p = (int) frac;
1115 frac -= p;
1116 aircraft->projectedPos[0] = (1 - frac) * aircraft->route.point[p][0] + frac * aircraft->route.point[p + 1][0];
1117 aircraft->projectedPos[1] = (1 - frac) * aircraft->route.point[p][1] + frac * aircraft->route.point[p + 1][1];
1118
1119 MAP_CheckPositionBoundaries(aircraft->projectedPos);
1120 }
1121
1122 return false;
1123}
1124
1125static void AIR_Move (aircraft_t* aircraft, int deltaTime)
1126{
1127 /* Aircraft is moving */
1128 if (AIR_AircraftMakeMove(deltaTime, aircraft)) {
1129 /* aircraft reach its destination */
1130 const float *end = aircraft->route.point[aircraft->route.numPoints - 1];
1131 Vector2Copy(end, aircraft->pos)((aircraft->pos)[0]=(end)[0],(aircraft->pos)[1]=(end)[1
])
;
1132 MAP_CheckPositionBoundaries(aircraft->pos);
1133
1134 switch (aircraft->status) {
1135 case AIR_MISSION:
1136 /* Aircraft reached its mission */
1137 assert(aircraft->mission)(__builtin_expect(!(aircraft->mission), 0) ? __assert_rtn(
__func__, "src/client/cgame/campaign/cp_aircraft.cpp", 1137, "aircraft->mission"
) : (void)0)
;
1138 aircraft->mission->active = true;
1139 aircraft->status = AIR_DROP;
1140 MAP_SetMissionAircraft(aircraft)(ccs.geoscape.missionAircraft = (aircraft));
1141 MAP_SelectMission(aircraft->mission);
1142 MAP_SetInterceptorAircraft(aircraft)(ccs.geoscape.interceptAircraft = (aircraft));
1143 CP_GameTimeStop();
1144 cgi->UI_PushWindow("popup_intercept_ready");
1145 break;
1146 case AIR_RETURNING:
1147 /* aircraft entered in homebase */
1148 aircraft->status = AIR_REFUEL;
1149 B_AircraftReturnedToHomeBase(aircraft);
1150 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer)(sizeof(cp_messageBuffer) / sizeof(*(cp_messageBuffer))),
1151 _("Craft %s has returned to %s.")gettext("Craft %s has returned to %s."), aircraft->name, aircraft->homebase->name);
1152 MSO_CheckAddNewMessage(NT_AIRCRAFT_ARRIVEDHOME, _("Notice")gettext("Notice"), cp_messageBuffer);
1153 break;
1154 case AIR_TRANSFER:
1155 case AIR_UFO:
1156 break;
1157 default:
1158 aircraft->status = AIR_IDLE;
1159 break;
1160 }
1161 }
1162}
1163
1164static void AIR_Refuel (aircraft_t *aircraft, int deltaTime)
1165{
1166 /* Aircraft is refuelling at base */
1167 int fillup;
1168
1169 if (aircraft->fuel < 0)
1170 aircraft->fuel = 0;
1171 /* amount of fuel we would like to load */
1172 fillup = std::min(deltaTime * AIRCRAFT_REFUEL_FACTOR16, aircraft->stats[AIR_STATS_FUELSIZE] - aircraft->fuel);
1173 /* This craft uses antimatter as fuel */
1174 assert(aircraft->homebase)(__builtin_expect(!(aircraft->homebase), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_aircraft.cpp", 1174,
"aircraft->homebase") : (void)0)
;
1175 if (aircraft->stats[AIR_STATS_ANTIMATTER] > 0 && fillup > 0) {
1176 /* the antimatter we have */
1177 const int amAvailable = B_ItemInBase(INVSH_GetItemByID(ANTIMATTER_TECH_ID"antimatter"), aircraft->homebase);
1178 /* current antimatter level in craft */
1179 const int amCurrentLevel = aircraft->stats[AIR_STATS_ANTIMATTER] * (aircraft->fuel / (float) aircraft->stats[AIR_STATS_FUELSIZE]);
1180 /* next antimatter level in craft */
1181 const int amNextLevel = aircraft->stats[AIR_STATS_ANTIMATTER] * ((aircraft->fuel + fillup) / (float) aircraft->stats[AIR_STATS_FUELSIZE]);
1182 /* antimatter needed */
1183 int amLoad = amNextLevel - amCurrentLevel;
1184
1185 if (amLoad > amAvailable) {
1186 /* amount of fuel we can load */
1187 fillup = aircraft->stats[AIR_STATS_FUELSIZE] * ((amCurrentLevel + amAvailable) / (float) aircraft->stats[AIR_STATS_ANTIMATTER]) - aircraft->fuel;
1188 amLoad = amAvailable;
1189
1190 if (!aircraft->notifySent[AIR_CANNOT_REFUEL]) {
1191 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer)(sizeof(cp_messageBuffer) / sizeof(*(cp_messageBuffer))),
1192 _("Craft %s couldn't be completely refueled at %s. Not enough antimatter.")gettext("Craft %s couldn't be completely refueled at %s. Not enough antimatter."
)
, aircraft->name, aircraft->homebase->name);
1193 MSO_CheckAddNewMessage(NT_AIRCRAFT_CANNOTREFUEL, _("Notice")gettext("Notice"), cp_messageBuffer);
1194 aircraft->notifySent[AIR_CANNOT_REFUEL] = true;
1195 }
1196 }
1197
1198 if (amLoad > 0)
1199 B_ManageAntimatter(aircraft->homebase, amLoad, false);
1200 }
1201
1202 aircraft->fuel += fillup;
1203
1204 if (aircraft->fuel >= aircraft->stats[AIR_STATS_FUELSIZE]) {
1205 aircraft->fuel = aircraft->stats[AIR_STATS_FUELSIZE];
1206 aircraft->status = AIR_HOME;
1207 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer)(sizeof(cp_messageBuffer) / sizeof(*(cp_messageBuffer))),
1208 _("Craft %s has refueled at %s.")gettext("Craft %s has refueled at %s."), aircraft->name, aircraft->homebase->name);
1209 MSO_CheckAddNewMessage(NT_AIRCRAFT_REFUELED, _("Notice")gettext("Notice"), cp_messageBuffer);
1210 aircraft->notifySent[AIR_CANNOT_REFUEL] = false;
1211 }
1212
1213}
1214
1215/**
1216 * @brief Handles aircraft movement and actions in geoscape mode
1217 * @sa CP_CampaignRun
1218 * @param[in] campaign The campaign data structure
1219 * @param[in] dt time delta (may be 0 if radar overlay should be updated but no aircraft moves)
1220 * @param[in] updateRadarOverlay True if radar overlay should be updated (not needed if geoscape isn't updated)
1221 */
1222void AIR_CampaignRun (const campaign_t* campaign, int dt, bool updateRadarOverlay)
1223{
1224 /* true if at least one aircraft moved: radar overlay must be updated
1225 * This is static because aircraft can move without radar being
1226 * updated (sa CP_CampaignRun) */
1227 static bool radarOverlayReset = false;
1228
1229 /* Run each aircraft */
1230 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
1231 int k;
1232
1233 if (aircraft->status == AIR_CRASHED)
1234 continue;
1235
1236 assert(aircraft->homebase)(__builtin_expect(!(aircraft->homebase), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_aircraft.cpp", 1236,
"aircraft->homebase") : (void)0)
;
1237 if (aircraft->status == AIR_IDLE) {
1238 /* Aircraft idle out of base */
1239 aircraft->fuel -= dt;
1240 } else if (AIR_IsAircraftOnGeoscape(aircraft)) {
1241 AIR_Move(aircraft, dt);
1242 /* radar overlay should be updated */
1243 radarOverlayReset = true;
1244 } else if (aircraft->status == AIR_REFUEL) {
1245 AIR_Refuel(aircraft, dt);
1246 }
1247
1248 /* Check aircraft low fuel (only if aircraft is not already returning to base or in base) */
1249 if (aircraft->status != AIR_RETURNING && AIR_IsAircraftOnGeoscape(aircraft) &&
1250 !AIR_AircraftHasEnoughFuel(aircraft, aircraft->pos)) {
1251 /** @todo check if aircraft can go to a closer base with free space */
1252 MS_AddNewMessage(_("Notice")gettext("Notice"), va(_("Craft %s is low on fuel and must return to base.")gettext("Craft %s is low on fuel and must return to base."), aircraft->name));
1253 AIR_AircraftReturnToBase(aircraft);
1254 }
1255
1256 /* Aircraft purchasing ufo */
1257 if (aircraft->status == AIR_UFO) {
1258 /* Solve the fight */
1259 AIRFIGHT_ExecuteActions(campaign, aircraft, aircraft->aircraftTarget);
1260 }
1261
1262 for (k = 0; k < aircraft->maxWeapons; k++) {
1263 /* Update delay to launch next projectile */
1264 if (AIR_IsAircraftOnGeoscape(aircraft) && (aircraft->weapons[k].delayNextShot > 0))
1265 aircraft->weapons[k].delayNextShot -= dt;
1266 /* Reload if needed */
1267 if (aircraft->weapons[k].ammoLeft <= 0)
1268 AII_ReloadWeapon(&aircraft->weapons[k]);
1269 }
1270 }
1271
1272 if (updateRadarOverlay && radarOverlayReset && MAP_IsRadarOverlayActivated()) {
1273 RADAR_UpdateWholeRadarOverlay();
1274 radarOverlayReset = false;
1275 }
1276}
1277
1278/**
1279 * @brief Returns aircraft for a given global index.
1280 * @param[in] aircraftIdx Global aircraft index.
1281 * @return An aircraft pointer (to a struct in a base) that has the given index or NULL if no aircraft found.
1282 */
1283aircraft_t* AIR_AircraftGetFromIDX (int aircraftIdx)
1284{
1285 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
1286 if (aircraft->idx == aircraftIdx) {
1287 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_AircraftGetFromIDX: aircraft idx: %i\n", aircraft->idx);
1288 return aircraft;
1289 }
1290 }
1291
1292 return NULL__null;
1293}
1294
1295/**
1296 * @brief Sends the specified aircraft to specified mission.
1297 * @param[in] aircraft Pointer to aircraft to send to mission.
1298 * @param[in] mission Pointer to given mission.
1299 * @return true if sending an aircraft to specified mission is possible.
1300 */
1301bool AIR_SendAircraftToMission (aircraft_t *aircraft, mission_t *mission)
1302{
1303 if (!aircraft || !mission)
1304 return false;
1305
1306 if (AIR_GetTeamSize(aircraft) == 0) {
1307 CP_Popup(_("Notice")gettext("Notice"), _("Assign one or more soldiers to this aircraft first.")gettext("Assign one or more soldiers to this aircraft first."
)
);
1308 return false;
1309 }
1310
1311 /* if aircraft was in base */
1312 if (AIR_IsAircraftInBase(aircraft)) {
1313 /* reload its ammunition */
1314 AII_ReloadAircraftWeapons(aircraft);
1315 }
1316
1317 /* ensure interceptAircraft is set correctly */
1318 MAP_SetInterceptorAircraft(aircraft)(ccs.geoscape.interceptAircraft = (aircraft));
1319
1320 /* if mission is a base-attack and aircraft already in base, launch
1321 * mission immediately */
1322 if (B_IsUnderAttack(aircraft->homebase)((aircraft->homebase)->baseStatus == BASE_UNDER_ATTACK) && AIR_IsAircraftInBase(aircraft)) {
1323 aircraft->mission = mission;
1324 mission->active = true;
1325 cgi->UI_PushWindow("popup_baseattack");
1326 return true;
1327 }
1328
1329 if (!AIR_AircraftHasEnoughFuel(aircraft, mission->pos)) {
1330 MS_AddNewMessage(_("Notice")gettext("Notice"), _("Insufficient fuel.")gettext("Insufficient fuel."));
1331 return false;
1332 }
1333
1334 MAP_MapCalcLine(aircraft->pos, mission->pos, &aircraft->route);
1335 aircraft->status = AIR_MISSION;
1336 aircraft->time = 0;
1337 aircraft->point = 0;
1338 aircraft->mission = mission;
1339
1340 return true;
1341}
1342
1343/**
1344 * @brief Initialise all values of an aircraft slot.
1345 * @param[in] aircraftTemplate Pointer to the aircraft which needs initalisation of its slots.
1346 */
1347static void AII_InitialiseAircraftSlots (aircraft_t *aircraftTemplate)
1348{
1349 int i;
1350
1351 /* initialise weapon slots */
1352 for (i = 0; i < MAX_AIRCRAFTSLOT4; i++) {
1353 AII_InitialiseSlot(aircraftTemplate->weapons + i, aircraftTemplate, NULL__null, NULL__null, AC_ITEM_WEAPON);
1354 AII_InitialiseSlot(aircraftTemplate->electronics + i, aircraftTemplate, NULL__null, NULL__null, AC_ITEM_ELECTRONICS);
1355 }
1356 AII_InitialiseSlot(&aircraftTemplate->shield, aircraftTemplate, NULL__null, NULL__null, AC_ITEM_SHIELD);
1357}
1358
1359/**
1360 * @brief List of valid strings for itemPos_t
1361 * @note must be in the same order than @c itemPos_t
1362 */
1363static char const* const air_position_strings[] = {
1364 "nose_left",
1365 "nose_center",
1366 "nose_right",
1367 "wing_left",
1368 "wing_right",
1369 "rear_left",
1370 "rear_center",
1371 "rear_right"
1372};
1373
1374/** @brief Valid aircraft parameter definitions from script files.
1375 * @note wrange can't be parsed in aircraft definition: this is a property
1376 * of weapon */
1377static const value_t aircraft_param_vals[] = {
1378 {"speed", V_INT, offsetof(aircraft_t, stats[AIR_STATS_SPEED])__builtin_offsetof(aircraft_t, stats[AIR_STATS_SPEED]), MEMBER_SIZEOF(aircraft_t, stats[0])sizeof(((aircraft_t *)0)->stats[0])},
1379 {"maxspeed", V_INT, offsetof(aircraft_t, stats[AIR_STATS_MAXSPEED])__builtin_offsetof(aircraft_t, stats[AIR_STATS_MAXSPEED]), MEMBER_SIZEOF(aircraft_t, stats[0])sizeof(((aircraft_t *)0)->stats[0])},
1380 {"shield", V_INT, offsetof(aircraft_t, stats[AIR_STATS_SHIELD])__builtin_offsetof(aircraft_t, stats[AIR_STATS_SHIELD]), MEMBER_SIZEOF(aircraft_t, stats[0])sizeof(((aircraft_t *)0)->stats[0])},
1381 {"ecm", V_INT, offsetof(aircraft_t, stats[AIR_STATS_ECM])__builtin_offsetof(aircraft_t, stats[AIR_STATS_ECM]), MEMBER_SIZEOF(aircraft_t, stats[0])sizeof(((aircraft_t *)0)->stats[0])},
1382 {"damage", V_INT, offsetof(aircraft_t, stats[AIR_STATS_DAMAGE])__builtin_offsetof(aircraft_t, stats[AIR_STATS_DAMAGE]), MEMBER_SIZEOF(aircraft_t, stats[0])sizeof(((aircraft_t *)0)->stats[0])},
1383 {"accuracy", V_INT, offsetof(aircraft_t, stats[AIR_STATS_ACCURACY])__builtin_offsetof(aircraft_t, stats[AIR_STATS_ACCURACY]), MEMBER_SIZEOF(aircraft_t, stats[0])sizeof(((aircraft_t *)0)->stats[0])},
1384 {"antimatter", V_INT, offsetof(aircraft_t, stats[AIR_STATS_ANTIMATTER])__builtin_offsetof(aircraft_t, stats[AIR_STATS_ANTIMATTER]), MEMBER_SIZEOF(aircraft_t, stats[0])sizeof(((aircraft_t *)0)->stats[0])},
1385
1386 {NULL__null, V_NULL, 0, 0}
1387};
1388
1389/** @brief Valid aircraft definition values from script files. */
1390static const value_t aircraft_vals[] = {
1391 {"name", V_STRING, offsetof(aircraft_t, name)__builtin_offsetof(aircraft_t, name), 0},
1392 {"defaultname", V_TRANSLATION_STRING, offsetof(aircraft_t, defaultName)__builtin_offsetof(aircraft_t, defaultName), 0},
1393 {"numteam", V_INT, offsetof(aircraft_t, maxTeamSize)__builtin_offsetof(aircraft_t, maxTeamSize), MEMBER_SIZEOF(aircraft_t, maxTeamSize)sizeof(((aircraft_t *)0)->maxTeamSize)},
1394 {"size", V_INT, offsetof(aircraft_t, size)__builtin_offsetof(aircraft_t, size), MEMBER_SIZEOF(aircraft_t, size)sizeof(((aircraft_t *)0)->size)},
1395 {"nogeoscape", V_BOOL, offsetof(aircraft_t, notOnGeoscape)__builtin_offsetof(aircraft_t, notOnGeoscape), MEMBER_SIZEOF(aircraft_t, notOnGeoscape)sizeof(((aircraft_t *)0)->notOnGeoscape)},
1396 {"interestlevel", V_INT, offsetof(aircraft_t, ufoInterestOnGeoscape)__builtin_offsetof(aircraft_t, ufoInterestOnGeoscape), MEMBER_SIZEOF(aircraft_t, ufoInterestOnGeoscape)sizeof(((aircraft_t *)0)->ufoInterestOnGeoscape)},
1397
1398 {"image", V_HUNK_STRING, offsetof(aircraft_t, image)__builtin_offsetof(aircraft_t, image), 0},
1399 {"model", V_HUNK_STRING, offsetof(aircraft_t, model)__builtin_offsetof(aircraft_t, model), 0},
1400 /* price for selling/buying */
1401 {"price", V_INT, offsetof(aircraft_t, price)__builtin_offsetof(aircraft_t, price), MEMBER_SIZEOF(aircraft_t, price)sizeof(((aircraft_t *)0)->price)},
1402 /* this is needed to let the buy and sell screen look for the needed building */
1403 /* to place the aircraft in */
1404 {"productioncost", V_INT, offsetof(aircraft_t, productionCost)__builtin_offsetof(aircraft_t, productionCost), MEMBER_SIZEOF(aircraft_t, productionCost)sizeof(((aircraft_t *)0)->productionCost)},
1405 {"building", V_HUNK_STRING, offsetof(aircraft_t, building)__builtin_offsetof(aircraft_t, building), 0},
1406
1407 {NULL__null, V_NULL, 0, 0}
1408};
1409
1410/**
1411 * @brief Parses all aircraft that are defined in our UFO-scripts.
1412 * @sa CL_ParseClientData
1413 * @sa CL_ParseScriptSecond
1414 * @note parses the aircraft into our aircraft_sample array to use as reference
1415 */
1416void AIR_ParseAircraft (const char *name, const char **text, bool assignAircraftItems)
1417{
1418 const char *errhead = "AIR_ParseAircraft: unexpected end of file (aircraft ";
1419 aircraft_t *aircraftTemplate;
1420 const char *token;
1421 int i;
1422 technology_t *tech;
1423 aircraftItemType_t itemType = MAX_ACITEMS;
1424
1425 if (ccs.numAircraftTemplates >= MAX_AIRCRAFT64) {
1426 Com_Printf("AIR_ParseAircraft: too many aircraft definitions; def \"%s\" ignored\n", name);
1427 return;
1428 }
1429
1430 if (!assignAircraftItems) {
1431 aircraftTemplate = NULL__null;
1432 for (i = 0; i < ccs.numAircraftTemplates; i++) {
1433 aircraft_t *aircraft = &ccs.aircraftTemplates[i];
1434 if (Q_streq(aircraft->id, name)(strcmp(aircraft->id, name) == 0)) {
1435 aircraftTemplate = aircraft;
1436 break;
1437 }
1438 }
1439
1440 if (aircraftTemplate) {
1441 Com_Printf("AIR_ParseAircraft: Second aircraft with same name found (%s) - second ignored\n", name);
1442 return;
1443 }
1444
1445 /* initialize the menu */
1446 aircraftTemplate = &ccs.aircraftTemplates[ccs.numAircraftTemplates];
1447 OBJZERO(*aircraftTemplate)(memset(&((*aircraftTemplate)), (0), sizeof((*aircraftTemplate
))))
;
1448
1449 Com_DPrintf(DEBUG_CLIENT0x20, "...found aircraft %s\n", name);
1450 aircraftTemplate->tpl = aircraftTemplate;
1451 aircraftTemplate->id = Mem_PoolStrDup(name, cp_campaignPool, 0)_Mem_PoolStrDup((name),(cp_campaignPool),(0),"src/client/cgame/campaign/cp_aircraft.cpp"
,1451)
;
1452 aircraftTemplate->status = AIR_HOME;
1453 /* default is no ufo */
1454 aircraftTemplate->ufotype = UFO_MAX;
1455 aircraftTemplate->maxWeapons = 0;
1456 aircraftTemplate->maxElectronics = 0;
1457 AII_InitialiseAircraftSlots(aircraftTemplate);
1458 /* Initialise UFO sensored */
1459 RADAR_InitialiseUFOs(&aircraftTemplate->radar);
1460
1461 ccs.numAircraftTemplates++;
1462 } else {
1463 aircraftTemplate = NULL__null;
1464 for (i = 0; i < ccs.numAircraftTemplates; i++) {
1465 aircraft_t *aircraft = &ccs.aircraftTemplates[i];
1466 if (Q_streq(aircraft->id, name)(strcmp(aircraft->id, name) == 0)) {
1467 aircraftTemplate = aircraft;
1468 break;
1469 }
1470 }
1471 if (!aircraftTemplate)
1472 Sys_Error("Could not find aircraft '%s'", name);
1473 }
1474
1475 /* get it's body */
1476 token = Com_Parse(text);
1477
1478 if (!*text || *token != '{') {
1479 Com_Printf("AIR_ParseAircraft: aircraft def \"%s\" without body ignored\n", name);
1480 return;
1481 }
1482
1483 do {
1484 token = Com_EParse(text, errhead, name);
1485 if (!*text)
1486 break;
1487 if (*token == '}')
1488 break;
1489
1490 if (Q_streq(token, "name")(strcmp(token, "name") == 0)) {
1491 token = Com_EParse(text, errhead, name);
1492 if (!*text)
1493 return;
1494 if (token[0] == '_')
1495 token++;
1496 Q_strncpyz(aircraftTemplate->name, token, sizeof(aircraftTemplate->name))Q_strncpyzDebug( aircraftTemplate->name, token, sizeof(aircraftTemplate
->name), "src/client/cgame/campaign/cp_aircraft.cpp", 1496
)
;
1497 continue;
1498 }
1499 if (assignAircraftItems) {
1500 /* write into cp_campaignPool - this data is reparsed on every new game */
1501 /* blocks like param { [..] } - otherwise we would leave the loop too early */
1502 if (*token == '{') {
1503 FS_SkipBlock(text);
1504 } else if (Q_streq(token, "shield")(strcmp(token, "shield") == 0)) {
1505 token = Com_EParse(text, errhead, name);
1506 if (!*text)
1507 return;
1508 Com_DPrintf(DEBUG_CLIENT0x20, "use shield %s for aircraft %s\n", token, aircraftTemplate->id);
1509 tech = RS_GetTechByID(token);
1510 if (tech)
1511 aircraftTemplate->shield.item = INVSH_GetItemByID(tech->provides);
1512 } else if (Q_streq(token, "slot")(strcmp(token, "slot") == 0)) {
1513 token = Com_EParse(text, errhead, name);
1514 if (!*text || *token != '{') {
1515 Com_Printf("AIR_ParseAircraft: Invalid slot value for aircraft: %s\n", name);
1516 return;
1517 }
1518 do {
1519 token = Com_EParse(text, errhead, name);
1520 if (!*text)
1521 break;
1522 if (*token == '}')
1523 break;
1524
1525 if (Q_streq(token, "type")(strcmp(token, "type") == 0)) {
1526 token = Com_EParse(text, errhead, name);
1527 if (!*text)
1528 return;
1529 for (i = 0; i < MAX_ACITEMS; i++) {
1530 if (Q_streq(token, air_slot_type_strings[i])(strcmp(token, air_slot_type_strings[i]) == 0)) {
1531 itemType = (aircraftItemType_t)i;
1532 switch (itemType) {
1533 case AC_ITEM_WEAPON:
1534 aircraftTemplate->maxWeapons++;
1535 break;
1536 case AC_ITEM_ELECTRONICS:
1537 aircraftTemplate->maxElectronics++;
1538 break;
1539 default:
1540 itemType = MAX_ACITEMS;
1541 }
1542 break;
1543 }
1544 }
1545 if (i == MAX_ACITEMS)
1546 Com_Error(ERR_DROP1, "Unknown value '%s' for slot type\n", token);
1547 } else if (Q_streq(token, "position")(strcmp(token, "position") == 0)) {
1548 token = Com_EParse(text, errhead, name);
1549 if (!*text)
1550 return;
1551 for (i = 0; i < AIR_POSITIONS_MAX; i++) {
1552 if (Q_streq(token, air_position_strings[i])(strcmp(token, air_position_strings[i]) == 0)) {
1553 switch (itemType) {
1554 case AC_ITEM_WEAPON:
1555 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].pos = (itemPos_t)i;
1556 break;
1557 case AC_ITEM_ELECTRONICS:
1558 aircraftTemplate->electronics[aircraftTemplate->maxElectronics - 1].pos = (itemPos_t)i;
1559 break;
1560 default:
1561 i = AIR_POSITIONS_MAX;
1562 break;
1563 }
1564 break;
1565 }
1566 }
1567 if (i == AIR_POSITIONS_MAX)
1568 Com_Error(ERR_DROP1, "Unknown value '%s' for slot position\n", token);
1569 } else if (Q_streq(token, "contains")(strcmp(token, "contains") == 0)) {
1570 token = Com_EParse(text, errhead, name);
1571 if (!*text)
1572 return;
1573 tech = RS_GetTechByID(token);
1574 if (tech) {
1575 switch (itemType) {
1576 case AC_ITEM_WEAPON:
1577 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].item = INVSH_GetItemByID(tech->provides);
1578 Com_DPrintf(DEBUG_CLIENT0x20, "use weapon %s for aircraft %s\n", token, aircraftTemplate->id);
1579 break;
1580 case AC_ITEM_ELECTRONICS:
1581 aircraftTemplate->electronics[aircraftTemplate->maxElectronics - 1].item = INVSH_GetItemByID(tech->provides);
1582 Com_DPrintf(DEBUG_CLIENT0x20, "use electronics %s for aircraft %s\n", token, aircraftTemplate->id);
1583 break;
1584 default:
1585 Com_Printf("Ignoring item value '%s' due to unknown slot type\n", token);
1586 }
1587 }
1588 } else if (Q_streq(token, "ammo")(strcmp(token, "ammo") == 0)) {
1589 token = Com_EParse(text, errhead, name);
1590 if (!*text)
1591 return;
1592 tech = RS_GetTechByID(token);
1593 if (tech) {
1594 if (itemType == AC_ITEM_WEAPON) {
1595 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].ammo = INVSH_GetItemByID(tech->provides);
1596 Com_DPrintf(DEBUG_CLIENT0x20, "use ammo %s for aircraft %s\n", token, aircraftTemplate->id);
1597 } else
1598 Com_Printf("Ignoring ammo value '%s' due to unknown slot type\n", token);
1599 }
1600 } else if (Q_streq(token, "size")(strcmp(token, "size") == 0)) {
1601 token = Com_EParse(text, errhead, name);
1602 if (!*text)
1603 return;
1604 if (itemType == AC_ITEM_WEAPON) {
1605 if (Q_streq(token, "light")(strcmp(token, "light") == 0))
1606 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].size = ITEM_LIGHT;
1607 else if (Q_streq(token, "medium")(strcmp(token, "medium") == 0))
1608 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].size = ITEM_MEDIUM;
1609 else if (Q_streq(token, "heavy")(strcmp(token, "heavy") == 0))
1610 aircraftTemplate->weapons[aircraftTemplate->maxWeapons - 1].size = ITEM_HEAVY;
1611 else
1612 Com_Printf("Unknown size value for aircraft slot: '%s'\n", token);
1613 } else
1614 Com_Printf("Ignoring size parameter '%s' for non-weapon aircraft slots\n", token);
1615 } else
1616 Com_Printf("AIR_ParseAircraft: Ignoring unknown slot value '%s'\n", token);
1617 } while (*text); /* dummy condition */
1618 }
1619 } else {
1620 if (Q_streq(token, "shield")(strcmp(token, "shield") == 0)) {
1621 Com_EParse(text, errhead, name);
1622 continue;
1623 }
1624 /* check for some standard values */
1625 if (Com_ParseBlockToken(name, text, aircraftTemplate, aircraft_vals, cp_campaignPool, token))
1626 continue;
1627
1628 if (Q_streq(token, "type")(strcmp(token, "type") == 0)) {
1629 token = Com_EParse(text, errhead, name);
1630 if (!*text)
1631 return;
1632 if (Q_streq(token, "transporter")(strcmp(token, "transporter") == 0))
1633 aircraftTemplate->type = AIRCRAFT_TRANSPORTER;
1634 else if (Q_streq(token, "interceptor")(strcmp(token, "interceptor") == 0))
1635 aircraftTemplate->type = AIRCRAFT_INTERCEPTOR;
1636 else if (Q_streq(token, "ufo")(strcmp(token, "ufo") == 0)) {
1637 aircraftTemplate->type = AIRCRAFT_UFO;
1638 aircraftTemplate->ufotype = Com_UFOShortNameToID(aircraftTemplate->id);
1639 }
1640 } else if (Q_streq(token, "slot")(strcmp(token, "slot") == 0)) {
1641 token = Com_EParse(text, errhead, name);
1642 if (!*text || *token != '{') {
1643 Com_Printf("AIR_ParseAircraft: Invalid slot value for aircraft: %s\n", name);
1644 return;
1645 }
1646 FS_SkipBlock(text);
1647 } else if (Q_streq(token, "param")(strcmp(token, "param") == 0)) {
1648 token = Com_EParse(text, errhead, name);
1649 if (!*text || *token != '{') {
1650 Com_Printf("AIR_ParseAircraft: Invalid param value for aircraft: %s\n", name);
1651 return;
1652 }
1653 do {
1654 token = Com_EParse(text, errhead, name);
1655 if (!*text)
1656 break;
1657 if (*token == '}')
1658 break;
1659
1660 if (Q_streq(token, "range")(strcmp(token, "range") == 0)) {
1661 /* this is the range of aircraft, must be translated into fuel */
1662 token = Com_EParse(text, errhead, name);
1663 if (!*text)
1664 return;
1665 Com_EParseValue(aircraftTemplate, token, V_INT, offsetof(aircraft_t, stats[AIR_STATS_FUELSIZE]), MEMBER_SIZEOF(aircraft_t, stats[0]))Com_EParseValueDebug(aircraftTemplate, token, V_INT, __builtin_offsetof
(aircraft_t, stats[AIR_STATS_FUELSIZE]), sizeof(((aircraft_t *
)0)->stats[0]), "src/client/cgame/campaign/cp_aircraft.cpp"
, 1665)
;
1666 if (aircraftTemplate->stats[AIR_STATS_SPEED] == 0)
1667 Com_Error(ERR_DROP1, "AIR_ParseAircraft: speed value must be entered before range value");
1668 aircraftTemplate->stats[AIR_STATS_FUELSIZE] = (int) (2.0f * (float)SECONDS_PER_HOUR3600 * aircraftTemplate->stats[AIR_STATS_FUELSIZE]) /
1669 ((float) aircraftTemplate->stats[AIR_STATS_SPEED]);
1670 } else {
1671 if (!Com_ParseBlockToken(name, text, aircraftTemplate, aircraft_param_vals, cp_campaignPool, token))
1672 Com_Printf("AIR_ParseAircraft: Ignoring unknown param value '%s'\n", token);
1673 }
1674 } while (*text); /* dummy condition */
1675 } else {
1676 Com_Printf("AIR_ParseAircraft: unknown token \"%s\" ignored (aircraft %s)\n", token, name);
1677 Com_EParse(text, errhead, name);
1678 }
1679 } /* assignAircraftItems */
1680 } while (*text);
1681
1682 if (aircraftTemplate->productionCost == 0)
1683 aircraftTemplate->productionCost = aircraftTemplate->price;
1684
1685 if (aircraftTemplate->size < AIRCRAFT_SMALL || aircraftTemplate->size > AIRCRAFT_LARGE)
1686 Sys_Error("Invalid aircraft size given for '%s'", aircraftTemplate->id);
1687}
1688
1689#ifdef DEBUG1
1690void AIR_ListCraftIndexes_f (void)
1691{
1692 Com_Printf("globalIDX\t(Craftname)\n");
1693 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
1694 Com_Printf("%i\t(%s)\n", aircraft->idx, aircraft->name);
1695 }
1696}
1697
1698/**
1699 * @brief Debug function that prints aircraft to game console
1700 */
1701void AIR_ListAircraftSamples_f (void)
1702{
1703 int i = 0, max = ccs.numAircraftTemplates;
1704 const value_t *vp;
1705
1706 Com_Printf("%i aircraft\n", max);
1707 if (Cmd_Argc() == 2) {
1708 max = atoi(Cmd_Argv(1));
1709 if (max >= ccs.numAircraftTemplates || max < 0)
1710 return;
1711 i = max - 1;
1712 }
1713 for (; i < max; i++) {
1714 aircraft_t *aircraftTemplate = &ccs.aircraftTemplates[i];
1715 Com_Printf("aircraft: '%s'\n", aircraftTemplate->id);
1716 for (vp = aircraft_vals; vp->string; vp++) {
1717 Com_Printf("..%s: %s\n", vp->string, Com_ValueToStr(aircraftTemplate, vp->type, vp->ofs));
1718 }
1719 for (vp = aircraft_param_vals; vp->string; vp++) {
1720 Com_Printf("..%s: %s\n", vp->string, Com_ValueToStr(aircraftTemplate, vp->type, vp->ofs));
1721 }
1722 }
1723}
1724#endif
1725
1726/*===============================================
1727Aircraft functions related to UFOs or missions.
1728===============================================*/
1729
1730/**
1731 * @brief Notify aircraft that a mission has been removed.
1732 * @param[in] mission Pointer to the mission that has been removed.
1733 * @note Aircraft currently moving to the mission will be redirect to base
1734 */
1735void AIR_AircraftsNotifyMissionRemoved (const mission_t *const mission)
1736{
1737 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
1738 if (aircraft->mission == mission)
1739 AIR_AircraftReturnToBase(aircraft);
1740 }
1741}
1742
1743/**
1744 * @brief Notify that a UFO has been removed.
1745 * @param[in] ufo Pointer to UFO that has been removed.
1746 * @param[in] destroyed True if the UFO has been destroyed, false if it only landed.
1747 */
1748void AIR_AircraftsNotifyUFORemoved (const aircraft_t *const ufo, bool destroyed)
1749{
1750 base_t *base;
1751
1752 assert(ufo)(__builtin_expect(!(ufo), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 1752, "ufo") : (void)0)
;
1753
1754 /* Aircraft currently purchasing the specified ufo will be redirect to base */
1755 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
1756 if (ufo == aircraft->aircraftTarget) {
1757 AIR_AircraftReturnToBase(aircraft);
1758 } else if (destroyed && (ufo < aircraft->aircraftTarget)) {
1759 aircraft->aircraftTarget--;
1760 }
1761 }
1762
1763 /** @todo this should be in a BDEF_NotifyUFORemoved callback */
1764 base = NULL__null;
1765 while ((base = B_GetNext(base)) != NULL__null) {
1766 int i;
1767 /* Base currently targeting the specified ufo loose their target */
1768 for (i = 0; i < base->numBatteries; i++) {
1769 baseWeapon_t *baseWeapon = &base->batteries[i];
1770 if (baseWeapon->target == ufo)
1771 baseWeapon->target = NULL__null;
1772 else if (destroyed && (baseWeapon->target > ufo))
1773 baseWeapon->target--;
1774 }
1775 for (i = 0; i < base->numLasers; i++) {
1776 baseWeapon_t *baseWeapon = &base->lasers[i];
1777 if (baseWeapon->target == ufo)
1778 baseWeapon->target = NULL__null;
1779 else if (destroyed && (baseWeapon->target > ufo))
1780 baseWeapon->target--;
1781 }
1782 }
1783}
1784
1785/**
1786 * @brief Notify that a UFO disappear from radars.
1787 * @param[in] ufo Pointer to a UFO that has disappeared.
1788 * @note Aircraft currently pursuing the specified UFO will be redirected to base
1789 */
1790void AIR_AircraftsUFODisappear (const aircraft_t *const ufo)
1791{
1792 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
1793 if (aircraft->status == AIR_UFO && ufo == aircraft->aircraftTarget)
1794 AIR_AircraftReturnToBase(aircraft);
1795 }
1796}
1797
1798/**
1799 * @brief funtion we need to find roots.
1800 * @param[in] c angle SOT
1801 * @param[in] B angle STI
1802 * @param[in] speedRatio ratio of speed of shooter divided by speed of target.
1803 * @param[in] a angle TOI
1804 * @note S is the position of the shooter, T the position of the target,
1805 * D the destination of the target and I the interception point where shooter should reach target.
1806 * @return value of the function.
1807 */
1808static inline float AIR_GetDestinationFunction (const float c, const float B, const float speedRatio, float a)
1809{
1810 return pow(cos(a) - cos(speedRatio * a) * cos(c), 2.)
1811 - sin(c) * sin(c) * (sin(speedRatio * a) * sin(speedRatio * a) - sin(a) * sin(a) * sin(B) * sin(B));
1812}
1813
1814/**
1815 * @brief derivative of the funtion we need to find roots.
1816 * @param[in] c angle SOT
1817 * @param[in] B angle STI
1818 * @param[in] speedRatio ratio of speed of shooter divided by speed of target.
1819 * @param[in] a angle TOI
1820 * @note S is the position of the shooter, T the position of the target,
1821 * D the destination of the target and I the interception point where shooter should reach target.
1822 * @return value of the derivative function.
1823 */
1824static inline float AIR_GetDestinationDerivativeFunction (const float c, const float B, const float speedRatio, float a)
1825{
1826 return 2. * (cos(a) - cos(speedRatio * a) * cos(c)) * (- sin(a) + speedRatio * sin(speedRatio * a) * cos(c))
1827 - sin(c) * sin(c) * (speedRatio * sin(2. * speedRatio * a) - sin(2. * a) * sin(B) * sin(B));
1828}
1829
1830/**
1831 * @brief Find the roots of a function.
1832 * @param[in] c angle SOT
1833 * @param[in] B angle STI
1834 * @param[in] speedRatio ratio of speed of shooter divided by speed of target.
1835 * @param[in] start minimum value of the root to find
1836 * @note S is the position of the shooter, T the position of the target,
1837 * D the destination of the target and I the interception point where shooter should reach target.
1838 * @return root of the function.
1839 */
1840static float AIR_GetDestinationFindRoot (const float c, const float B, const float speedRatio, float start)
1841{
1842 const float BIG_STEP = .05; /**< step for rough calculation. this value must be short enough so
1843 * that we're sure there's only 1 root in this range. */
1844 const float PRECISION_ROOT = 0.000001; /**< precision of the calculation */
1845 const float MAXIMUM_VALUE_ROOT = 2. * M_PI3.14159265358979323846264338327950288; /**< maximum value of the root to search */
1846 float epsilon; /**< precision of current point */
1847 float begin, end, middle; /**< abscissa of the point */
1848 float fBegin, fEnd, fMiddle; /**< ordinate of the point */
1849 float fdBegin, fdEnd, fdMiddle; /**< derivative of the point */
1850
1851 /* there may be several solution, first try to find roughly the smallest one */
1852 end = start + PRECISION_ROOT / 10.; /* don't start at 0: derivative is 0 */
1853 fEnd = AIR_GetDestinationFunction(c, B, speedRatio, end);
1854 fdEnd = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, end);
1855
1856 do {
1857 begin = end;
1858 fBegin = fEnd;
1859 fdBegin = fdEnd;
1860 end = begin + BIG_STEP;
1861 if (end > MAXIMUM_VALUE_ROOT) {
1862 end = MAXIMUM_VALUE_ROOT;
1863 fEnd = AIR_GetDestinationFunction(c, B, speedRatio, end);
1864 break;
1865 }
1866 fEnd = AIR_GetDestinationFunction(c, B, speedRatio, end);
1867 fdEnd = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, end);
1868 } while (fBegin * fEnd > 0 && fdBegin * fdEnd > 0);
1869
1870 if (fBegin * fEnd > 0) {
1871 if (fdBegin * fdEnd < 0) {
1872 /* the sign of derivative changed: we could have a root somewhere
1873 * between begin and end: try to narrow down the root until fBegin * fEnd < 0 */
1874 middle = (begin + end) / 2.;
1875 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
1876 fdMiddle = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, middle);
1877 do {
1878 if (fdEnd * fdMiddle < 0) {
1879 /* root is bigger than middle */
1880 begin = middle;
1881 fBegin = fMiddle;
1882 fdBegin = fdMiddle;
1883 } else if (fdBegin * fdMiddle < 0) {
1884 /* root is smaller than middle */
1885 end = middle;
1886 fEnd = fMiddle;
1887 fdEnd = fdMiddle;
1888 } else {
1889 Com_Error(ERR_DROP1, "AIR_GetDestinationFindRoot: Error in calculation, can't find root");
1890 }
1891 middle = (begin + end) / 2.;
1892 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
1893 fdMiddle = AIR_GetDestinationDerivativeFunction(c, B, speedRatio, middle);
1894
1895 epsilon = end - middle ;
1896
1897 if (epsilon < PRECISION_ROOT) {
1898 /* this is only a root of the derivative: no root of the function itself
1899 * proceed with next value */
1900 return AIR_GetDestinationFindRoot(c, B, speedRatio, end);
1901 }
1902 } while (fBegin * fEnd > 0);
1903 } else {
1904 /* there's no solution, return default value */
1905 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_GetDestinationFindRoot: Did not find solution is range %.2f, %.2f\n", start, MAXIMUM_VALUE_ROOT);
1906 return -10.;
1907 }
1908 }
1909
1910 /* now use dichotomy to get more precision on the solution */
1911
1912 middle = (begin + end) / 2.;
1913 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
1914
1915 do {
1916 if (fEnd * fMiddle < 0) {
1917 /* root is bigger than middle */
1918 begin = middle;
1919 fBegin = fMiddle;
1920 } else if (fBegin * fMiddle < 0) {
1921 /* root is smaller than middle */
1922 end = middle;
1923 fEnd = fMiddle;
1924 } else {
1925 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_GetDestinationFindRoot: Error in calculation, one of the value is nan\n");
1926 return -10.;
1927 }
1928 middle = (begin + end) / 2.;
1929 fMiddle = AIR_GetDestinationFunction(c, B, speedRatio, middle);
1930
1931 epsilon = end - middle ;
1932 } while (epsilon > PRECISION_ROOT);
1933 return middle;
1934}
1935
1936/**
1937 * @brief Calculates the point where aircraft should go to intecept a moving target.
1938 * @param[in] shooter Pointer to shooting aircraft.
1939 * @param[in] target Pointer to target aircraft.
1940 * @param[out] dest Destination that shooting aircraft should aim to intercept target aircraft.
1941 * @todo only compute this calculation every time target changes destination, or one of the aircraft speed changes.
1942 * @sa AIR_SendAircraftPursuingUFO
1943 * @sa UFO_SendPursuingAircraft
1944 */
1945void AIR_GetDestinationWhilePursuing (const aircraft_t *shooter, const aircraft_t *target, vec2_t *dest)
1946{
1947 vec3_t shooterPos, targetPos, targetDestPos, shooterDestPos, rotationAxis;
1948 vec3_t tangentVectTS, tangentVectTD;
1949 float a, b, c, B;
1950
1951 const float speedRatio = (float)(shooter->stats[AIR_STATS_SPEED]) / target->stats[AIR_STATS_SPEED];
1952
1953 c = GetDistanceOnGlobe(shooter->pos, target->pos) * torad(3.14159265358979323846264338327950288/180.0f);
1954
1955 /* Convert aircraft position into cartesian frame */
1956 PolarToVec(shooter->pos, shooterPos);
1957 PolarToVec(target->pos, targetPos);
1958 PolarToVec(target->route.point[target->route.numPoints - 1], targetDestPos);
1959
1960 /** In the following, we note S the position of the shooter, T the position of the target,
1961 * D the destination of the target and I the interception point where shooter should reach target
1962 * O is the center of earth.
1963 * A, B and C are the angles TSI, STI and SIT
1964 * a, b, and c are the angles TOI, SOI and SOT
1965 *
1966 * According to geometry on a sphere, the values defined above must be solutions of both equations:
1967 * sin(A) / sin(a) = sin(B) / sin(b)
1968 * cos(a) = cos(b) * cos(c) + sin(b) * sin(c) * cos(A)
1969 * And we have another equation, given by the fact that shooter and target must reach I at the same time:
1970 * shooterSpeed * a = targetSpeed * b
1971 * We the want to find and equation linking a, c and B (we know the last 2 values). We therefore
1972 * eliminate b, then A, to get the equation we need to solve:
1973 * pow(cos(a) - cos(speedRatio * a) * cos(c), 2.)
1974 * - sin(c) * sin(c) * (sin(speedRatio * a) * sin(speedRatio * a) - sin(a) * sin(a) * sin(B) * sin(B)) = 0
1975 */
1976
1977 /* Get first vector (tangent to triangle in T, in the direction of D) */
1978 CrossProduct(targetPos, shooterPos, rotationAxis);
1979 VectorNormalize(rotationAxis);
1980 RotatePointAroundVector(tangentVectTS, rotationAxis, targetPos, 90.0f);
1981 /* Get second vector (tangent to triangle in T, in the direction of S) */
1982 CrossProduct(targetPos, targetDestPos, rotationAxis);
1983 VectorNormalize(rotationAxis);
1984 RotatePointAroundVector(tangentVectTD, rotationAxis, targetPos, 90.0f);
1985
1986 /* Get angle B of the triangle (in radian) */
1987 B = acos(DotProduct(tangentVectTS, tangentVectTD)((tangentVectTS)[0]*(tangentVectTD)[0]+(tangentVectTS)[1]*(tangentVectTD
)[1]+(tangentVectTS)[2]*(tangentVectTD)[2])
);
1988
1989 /* Look for a value, as long as we don't have a proper value */
1990 for (a = 0;;) {
4
Loop condition is true. Entering loop body
1991 a = AIR_GetDestinationFindRoot(c, B, speedRatio, a);
1992
1993 if (a < 0.) {
5
Taking true branch
1994 /* we couldn't find a root on the whole range */
1995 break;
6
Execution continues on line 2014
1996 }
1997
1998 /* Get rotation vector */
1999 CrossProduct(targetPos, targetDestPos, rotationAxis);
2000 VectorNormalize(rotationAxis);
2001
2002 /* Rotate target position of dist to find destination point */
2003 RotatePointAroundVector(shooterDestPos, rotationAxis, targetPos, a * todeg(180.0f/3.14159265358979323846264338327950288));
2004 VecToPolar(shooterDestPos, *dest);
2005
2006 b = GetDistanceOnGlobe(shooter->pos, *dest) * torad(3.14159265358979323846264338327950288/180.0f);
2007
2008 if (fabs(b - speedRatio * a) < .1)
2009 break;
2010
2011 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_GetDestinationWhilePursuing: reject solution: doesn't fit %.2f == %.2f\n", b, speedRatio * a);
2012 }
2013
2014 if (a < 0.) {
7
Taking false branch
2015 /* did not find solution, go directly to target direction */
2016 Vector2Copy(target->pos, (*dest))(((*dest))[0]=(target->pos)[0],((*dest))[1]=(target->pos
)[1])
;
2017 return;
2018 }
2019
2020 /** @todo add EQUAL_EPSILON here? */
2021 /* make sure we don't get a NAN value */
2022 assert((*dest)[0] <= 180.0f && (*dest)[0] >= -180.0f && (*dest)[1] <= 90.0f && (*dest)[1] >= -90.0f)(__builtin_expect(!((*dest)[0] <= 180.0f && (*dest
)[0] >= -180.0f && (*dest)[1] <= 90.0f &&
(*dest)[1] >= -90.0f), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2022, "(*dest)[0] <= 180.0f && (*dest)[0] >= -180.0f && (*dest)[1] <= 90.0f && (*dest)[1] >= -90.0f"
) : (void)0)
;
8
Within the expansion of the macro 'assert':
a
The left operand of '<=' is a garbage value
2023}
2024
2025/**
2026 * @brief Make the specified aircraft purchasing a UFO.
2027 * @param[in] aircraft Pointer to an aircraft which will hunt for a UFO.
2028 * @param[in] ufo Pointer to a UFO.
2029 */
2030bool AIR_SendAircraftPursuingUFO (aircraft_t* aircraft, aircraft_t* ufo)
2031{
2032 vec2_t dest;
2033
2034 if (!aircraft)
1
Taking false branch
2035 return false;
2036
2037 /* if aircraft was in base */
2038 if (AIR_IsAircraftInBase(aircraft)) {
2
Taking false branch
2039 /* reload its ammunition */
2040 AII_ReloadAircraftWeapons(aircraft);
2041 }
2042
2043 AIR_GetDestinationWhilePursuing(aircraft, ufo, &dest);
3
Calling 'AIR_GetDestinationWhilePursuing'
2044 /* check if aircraft has enough fuel */
2045 if (!AIR_AircraftHasEnoughFuel(aircraft, dest)) {
2046 /* did not find solution, go directly to target direction if enough fuel */
2047 if (AIR_AircraftHasEnoughFuel(aircraft, ufo->pos)) {
2048 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_SendAircraftPursuingUFO: not enough fuel to anticipate target movement: go directly to target position\n");
2049 Vector2Copy(ufo->pos, dest)((dest)[0]=(ufo->pos)[0],(dest)[1]=(ufo->pos)[1]);
2050 } else {
2051 MS_AddNewMessage(_("Notice")gettext("Notice"), va(_("Craft %s has not enough fuel to intercept UFO: fly back to %s.")gettext("Craft %s has not enough fuel to intercept UFO: fly back to %s."
)
, aircraft->name, aircraft->homebase->name));
2052 AIR_AircraftReturnToBase(aircraft);
2053 return false;
2054 }
2055 }
2056
2057 MAP_MapCalcLine(aircraft->pos, dest, &aircraft->route);
2058 aircraft->status = AIR_UFO;
2059 aircraft->time = 0;
2060 aircraft->point = 0;
2061 aircraft->aircraftTarget = ufo;
2062 return true;
2063}
2064
2065/*============================================
2066Aircraft functions related to team handling.
2067============================================*/
2068
2069/**
2070 * @brief Resets team in given aircraft.
2071 * @param[in] aircraft Pointer to an aircraft, where the team will be reset.
2072 */
2073void AIR_ResetAircraftTeam (aircraft_t *aircraft)
2074{
2075 LIST_Delete(&aircraft->acTeam);
2076}
2077
2078/**
2079 * @brief Adds given employee to given aircraft.
2080 * @param[in] aircraft Pointer to an aircraft, to which we will add employee.
2081 * @param[in] employee The employee to add to the aircraft.
2082 * @note this is responsible for adding soldiers to a team in dropship
2083 */
2084bool AIR_AddToAircraftTeam (aircraft_t *aircraft, employee_t* employee)
2085{
2086 if (!employee)
2087 return false;
2088
2089 if (!aircraft)
2090 return false;
2091
2092 if (AIR_GetTeamSize(aircraft) < aircraft->maxTeamSize) {
2093 LIST_AddPointer(&aircraft->acTeam, employee);
2094 return true;
2095 }
2096
2097 return false;
2098}
2099
2100/**
2101 * @brief Checks whether given employee is in given aircraft
2102 * @param[in] aircraft The aircraft to check
2103 * @param[in] employee Employee to check.
2104 * @return @c true if the given employee is assigned to the given aircraft.
2105 */
2106bool AIR_IsInAircraftTeam (const aircraft_t *aircraft, const employee_t *employee)
2107{
2108 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2108, "aircraft") : (void)0)
;
2109 assert(employee)(__builtin_expect(!(employee), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2109, "employee") : (void)0)
;
2110 return LIST_GetPointer(aircraft->acTeam, employee) != NULL__null;
2111}
2112
2113/**
2114 * @brief Counts the number of soldiers in given aircraft.
2115 * @param[in] aircraft Pointer to the aircraft, for which we return the amount of soldiers.
2116 * @return Amount of soldiers.
2117 */
2118int AIR_GetTeamSize (const aircraft_t *aircraft)
2119{
2120 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2120, "aircraft") : (void)0)
;
2121 return LIST_Count(aircraft->acTeam);
2122}
2123
2124/**
2125 * @brief Assign a pilot to an aircraft
2126 * @param[out] aircraft Pointer to the aircraft to add pilot to
2127 * @param[in] pilot Pointer to the pilot to add
2128 * @return @c true if the assignment was successful (there wasn't a pilot
2129 * assigned), @c false if there was already a pilot assigned and we tried
2130 * to assign a new one (@c pilot isn't @c NULL).
2131 */
2132bool AIR_SetPilot (aircraft_t *aircraft, employee_t *pilot)
2133{
2134 if (aircraft->pilot == NULL__null || pilot == NULL__null) {
2135 aircraft->pilot = pilot;
2136 return true;
2137 }
2138
2139 return false;
2140}
2141
2142/**
2143 * @brief Get pilot of an aircraft
2144 * @param[in] aircraft Pointer to the aircraft
2145 * @return @c NULL if there is no pilot assigned to this craft, the employee pointer otherwise
2146 */
2147employee_t* AIR_GetPilot (const aircraft_t *aircraft)
2148{
2149 const employee_t *e = aircraft->pilot;
2150
2151 if (!e)
2152 return NULL__null;
2153
2154 return E_GetEmployeeByTypeFromChrUCN(e->type, e->chr.ucn);
2155}
2156
2157/**
2158 * @brief Adds the pilot to the first available aircraft at the specified base.
2159 * @param[in] base Which base has aircraft to add the pilot to.
2160 * @param[in] pilot Which pilot to add.
2161 */
2162void AIR_AutoAddPilotToAircraft (const base_t* base, employee_t* pilot)
2163{
2164 AIR_ForeachFromBase(aircraft, base)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((base)) && (aircraft)->status != AIR_CRASHED
)) continue; else
{
2165 if (AIR_SetPilot(aircraft, pilot))
2166 break;
2167 }
2168}
2169
2170/**
2171 * @brief Checks to see if the pilot is in any aircraft at this base.
2172 * If he is then he is removed from that aircraft.
2173 * @param[in] base Which base has the aircraft to search for the employee in.
2174 * @param[in] pilot Which pilot to search for.
2175 */
2176void AIR_RemovePilotFromAssignedAircraft (const base_t* base, const employee_t* pilot)
2177{
2178 AIR_ForeachFromBase(aircraft, base)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else if (!((aircraft)->
homebase == ((base)) && (aircraft)->status != AIR_CRASHED
)) continue; else
{
2179 if (AIR_GetPilot(aircraft) == pilot) {
2180 AIR_SetPilot(aircraft, NULL__null);
2181 break;
2182 }
2183 }
2184}
2185
2186/**
2187 * @brief Get the all the unique weapon ranges of this aircraft.
2188 * @param[in] slot Pointer to the aircrafts weapon slot list.
2189 * @param[in] maxSlot maximum number of weapon slots in aircraft.
2190 * @param[out] weaponRanges An array containing a unique list of weapons ranges.
2191 * @return Number of unique weapons ranges.
2192 */
2193int AIR_GetAircraftWeaponRanges (const aircraftSlot_t *slot, int maxSlot, float *weaponRanges)
2194{
2195 int idxSlot;
2196 int idxAllWeap;
2197 float allWeaponRanges[MAX_AIRCRAFTSLOT4];
2198 int numAllWeaponRanges = 0;
2199 int numUniqueWeaponRanges = 0;
2200
2201 assert(slot)(__builtin_expect(!(slot), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2201, "slot") : (void)0)
;
2202
2203 /* We choose the usable weapon to add to the weapons array */
2204 for (idxSlot = 0; idxSlot < maxSlot; idxSlot++) {
2205 const aircraftSlot_t *weapon = slot + idxSlot;
2206 const objDef_t *ammo = weapon->ammo;
2207
2208 if (!ammo)
2209 continue;
2210
2211 allWeaponRanges[numAllWeaponRanges] = ammo->craftitem.stats[AIR_STATS_WRANGE];
2212 numAllWeaponRanges++;
2213 }
2214
2215 if (numAllWeaponRanges > 0) {
2216 /* sort the list of all weapon ranges and create an array with only the unique ranges */
2217 qsort(allWeaponRanges, numAllWeaponRanges, sizeof(allWeaponRanges[0]), Q_FloatSort);
2218
2219 for (idxAllWeap = 0; idxAllWeap < numAllWeaponRanges; idxAllWeap++) {
2220 if (allWeaponRanges[idxAllWeap] != weaponRanges[numUniqueWeaponRanges - 1] || idxAllWeap == 0) {
2221 weaponRanges[numUniqueWeaponRanges] = allWeaponRanges[idxAllWeap];
2222 numUniqueWeaponRanges++;
2223 }
2224 }
2225 }
2226
2227 return numUniqueWeaponRanges;
2228}
2229
2230/**
2231 * @brief Saves an route plan of an aircraft
2232 * @param[out] node XML Node structure, where we write the information to
2233 * @param[in] route Aircraft route plan
2234 */
2235static void AIR_SaveRouteXML (xmlNode_tmxml_node_t *node, const mapline_t *route)
2236{
2237 int j;
2238 xmlNode_tmxml_node_t *subnode;
2239
2240 subnode = XML_AddNode(node, SAVE_AIRCRAFT_ROUTE"route");
2241 XML_AddFloatValue(subnode, SAVE_AIRCRAFT_ROUTE_DISTANCE"distance", route->distance);
2242 for (j = 0; j < route->numPoints; j++) {
2243 XML_AddPos2(subnode, SAVE_AIRCRAFT_ROUTE_POINT"point", route->point[j]);
2244 }
2245}
2246
2247/**
2248 * @brief Saves an item slot
2249 * @param[in] slot Pointer to the slot where item is.
2250 * @param[in] num Number of slots for this aircraft.
2251 * @param[out] p XML Node structure, where we write the information to
2252 * @param[in] p pointer where information are written.
2253 * @param[in] weapon True if the slot is a weapon slot.
2254 * @sa B_Save
2255 * @sa AII_InitialiseSlot
2256 */
2257static void AIR_SaveAircraftSlotsXML (const aircraftSlot_t* slot, const int num, xmlNode_tmxml_node_t *p, bool weapon)
2258{
2259 int i;
2260
2261 for (i = 0; i < num; i++) {
2262 xmlNode_tmxml_node_t *sub = XML_AddNode(p, SAVE_AIRCRAFT_SLOT"slot");
2263 AII_SaveOneSlotXML(sub, &slot[i], weapon);
2264 }
2265}
2266
2267/**
2268 * @brief Saves an aircraft
2269 * @param[out] p XML Node structure, where we write the information to
2270 * @param[in] aircraft Aircraft we save
2271 * @param[in] isUfo If this aircraft is a UFO
2272 */
2273static bool AIR_SaveAircraftXML (xmlNode_tmxml_node_t *p, const aircraft_t* const aircraft, bool const isUfo)
2274{
2275 xmlNode_tmxml_node_t *node;
2276 xmlNode_tmxml_node_t *subnode;
2277 int l;
2278 const employee_t *pilot;
2279
2280 Com_RegisterConstList(saveAircraftConstants);
2281
2282 node = XML_AddNode(p, SAVE_AIRCRAFT_AIRCRAFT"craft");
2283
2284 XML_AddInt(node, SAVE_AIRCRAFT_IDX"idx", aircraft->idx);
2285 XML_AddString(node, SAVE_AIRCRAFT_ID"id", aircraft->id);
2286 XML_AddString(node, SAVE_AIRCRAFT_NAME"name", aircraft->name);
2287
2288 XML_AddString(node, SAVE_AIRCRAFT_STATUS"status", Com_GetConstVariable(SAVE_AIRCRAFTSTATUS_NAMESPACE"saveAircraftStatus", aircraft->status));
2289 XML_AddInt(node, SAVE_AIRCRAFT_FUEL"fuel", aircraft->fuel);
2290 XML_AddInt(node, SAVE_AIRCRAFT_DAMAGE"damage", aircraft->damage);
2291 XML_AddPos3(node, SAVE_AIRCRAFT_POS"pos", aircraft->pos);
2292 XML_AddPos3(node, SAVE_AIRCRAFT_DIRECTION"direction", aircraft->direction);
2293 XML_AddInt(node, SAVE_AIRCRAFT_POINT"point", aircraft->point);
2294 XML_AddInt(node, SAVE_AIRCRAFT_TIME"time", aircraft->time);
2295
2296 subnode = XML_AddNode(node, SAVE_AIRCRAFT_WEAPONS"weapons");
2297 AIR_SaveAircraftSlotsXML(aircraft->weapons, aircraft->maxWeapons, subnode, true);
2298 subnode = XML_AddNode(node, SAVE_AIRCRAFT_SHIELDS"shields");
2299 AIR_SaveAircraftSlotsXML(&aircraft->shield, 1, subnode, false);
2300 subnode = XML_AddNode(node, SAVE_AIRCRAFT_ELECTRONICS"electronics");
2301 AIR_SaveAircraftSlotsXML(aircraft->electronics, aircraft->maxElectronics, subnode, false);
2302
2303 AIR_SaveRouteXML(node, &aircraft->route);
2304
2305 if (isUfo) {
2306#ifdef DEBUG1
2307 if (!aircraft->mission)
2308 Com_Printf("Error: UFO '%s'is not linked to any mission\n", aircraft->id);
2309#endif
2310 XML_AddString(node, SAVE_AIRCRAFT_MISSIONID"missionid", aircraft->mission->id);
2311 /** detection id and time */
2312 XML_AddInt(node, SAVE_AIRCRAFT_DETECTIONIDX"detectionIDX", aircraft->detectionIdx);
2313 XML_AddDate(node, SAVE_AIRCRAFT_LASTSPOTTED_DATE"lastSpottedDate", aircraft->lastSpotted.day, aircraft->lastSpotted.sec);
2314 } else {
2315 if (aircraft->status == AIR_MISSION) {
2316 assert(aircraft->mission)(__builtin_expect(!(aircraft->mission), 0) ? __assert_rtn(
__func__, "src/client/cgame/campaign/cp_aircraft.cpp", 2316, "aircraft->mission"
) : (void)0)
;
2317 XML_AddString(node, SAVE_AIRCRAFT_MISSIONID"missionid", aircraft->mission->id);
2318 }
2319 if (aircraft->homebase) {
2320 XML_AddInt(node, SAVE_AIRCRAFT_HOMEBASE"homebase", aircraft->homebase->idx);
2321 }
2322 }
2323
2324 if (aircraft->aircraftTarget) {
2325 if (isUfo)
2326 XML_AddInt(node, SAVE_AIRCRAFT_AIRCRAFTTARGET"aircraftTarget", aircraft->aircraftTarget->idx);
2327 else
2328 XML_AddInt(node, SAVE_AIRCRAFT_AIRCRAFTTARGET"aircraftTarget", UFO_GetGeoscapeIDX(aircraft->aircraftTarget)((aircraft->aircraftTarget) - ccs.ufos));
2329 }
2330
2331 subnode = XML_AddNode(node, SAVE_AIRCRAFT_AIRSTATS"airstats");
2332 for (l = 0; l < AIR_STATS_MAX; l++) {
2333 xmlNode_tmxml_node_t *statNode;
2334#ifdef DEBUG1
2335 /* UFO HP can be < 0 if the UFO has been destroyed */
2336 if (!(isUfo && l == AIR_STATS_DAMAGE) && aircraft->stats[l] < 0)
2337 Com_Printf("Warning: ufo '%s' stats %i: %i is smaller than 0\n", aircraft->id, l, aircraft->stats[l]);
2338#endif
2339 if (aircraft->stats[l] != 0) {
2340 statNode = XML_AddNode(subnode, SAVE_AIRCRAFT_AIRSTAT"stat");
2341 XML_AddString(statNode, SAVE_AIRCRAFT_AIRSTATID"id", Com_GetConstVariable(SAVE_AIRCRAFTSTAT_NAMESPACE"saveAircraftStat", l));
2342 XML_AddLong(statNode, SAVE_AIRCRAFT_VAL"val", aircraft->stats[l]);
2343 }
2344 }
2345
2346 XML_AddBoolValue(node, SAVE_AIRCRAFT_DETECTED"detected", aircraft->detected);
2347 XML_AddBoolValue(node, SAVE_AIRCRAFT_LANDED"landed", aircraft->landed);
2348
2349 Com_UnregisterConstList(saveAircraftConstants);
2350
2351 /* All other informations are not needed for ufos */
2352 if (isUfo)
2353 return true;
2354
2355 XML_AddInt(node, SAVE_AIRCRAFT_HANGAR"hangar", aircraft->hangar);
2356
2357 subnode = XML_AddNode(node, SAVE_AIRCRAFT_AIRCRAFTTEAM"aircraftTeam");
2358 LIST_Foreach(aircraft->acTeam, employee_t, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (aircraft->acTeam); ! employee__break && employee__iter
;) for (employee_t* const employee = ( employee__break = employee__once
= true, (employee_t*) employee__iter->data); employee__once
; employee__break = employee__once = false) if ( employee__iter
= employee__iter->next, false) {} else
{
2359 xmlNode_tmxml_node_t *ssnode = XML_AddNode(subnode, SAVE_AIRCRAFT_MEMBER"member");
2360 XML_AddInt(ssnode, SAVE_AIRCRAFT_TEAM_UCN"ucn", employee->chr.ucn);
2361 }
2362
2363 pilot = AIR_GetPilot(aircraft);
2364 if (pilot)
2365 XML_AddInt(node, SAVE_AIRCRAFT_PILOTUCN"pilotUCN", pilot->chr.ucn);
2366
2367 /* itemcargo */
2368 subnode = XML_AddNode(node, SAVE_AIRCRAFT_CARGO"cargo");
2369 for (l = 0; l < aircraft->itemTypes; l++) {
2370 xmlNode_tmxml_node_t *ssnode = XML_AddNode(subnode, SAVE_AIRCRAFT_ITEM"item");
2371 assert(aircraft->itemcargo[l].item)(__builtin_expect(!(aircraft->itemcargo[l].item), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_aircraft.cpp", 2371,
"aircraft->itemcargo[l].item") : (void)0)
;
2372 XML_AddString(ssnode, SAVE_AIRCRAFT_ITEMID"itemid", aircraft->itemcargo[l].item->id);
2373 XML_AddInt(ssnode, SAVE_AIRCRAFT_AMOUNT"amount", aircraft->itemcargo[l].amount);
2374 }
2375
2376 /* aliencargo */
2377 {
2378 const int alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft)((aircraft)->alienCargoTypes);
2379 const aliensTmp_t *cargo = AL_GetAircraftAlienCargo(aircraft)(aircraft)->alienCargo;
2380 subnode = XML_AddNode(node, SAVE_AIRCRAFT_ALIENCARGO"alienCargo");
2381 for (l = 0; l < alienCargoTypes; l++) {
2382 xmlNode_tmxml_node_t *ssnode = XML_AddNode(subnode, SAVE_AIRCRAFT_CARGO"cargo");
2383 assert(cargo[l].teamDef)(__builtin_expect(!(cargo[l].teamDef), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aircraft.cpp", 2383, "cargo[l].teamDef"
) : (void)0)
;
2384 XML_AddString(ssnode, SAVE_AIRCRAFT_TEAMDEFID"teamdefid", cargo[l].teamDef->id);
2385 XML_AddIntValue(ssnode, SAVE_AIRCRAFT_ALIVE"alive", cargo[l].amountAlive);
2386 XML_AddIntValue(ssnode, SAVE_AIRCRAFT_DEAD"dead", cargo[l].amountDead);
2387 }
2388 }
2389
2390 return true;
2391}
2392
2393/**
2394 * @brief Save callback for savegames in xml format
2395 * @sa AIR_LoadXML
2396 * @sa B_SaveXML
2397 * @sa SAV_GameSaveXML
2398 */
2399bool AIR_SaveXML (xmlNode_tmxml_node_t *parent)
2400{
2401 int i;
2402 xmlNode_tmxml_node_t * node, *snode;
2403
2404 /* save phalanx aircraft */
2405 snode = XML_AddNode(parent, SAVE_AIRCRAFT_PHALANX"aircraft");
2406 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
2407 AIR_SaveAircraftXML(snode, aircraft, false);
2408 }
2409
2410 /* save the ufos on geoscape */
2411 snode = XML_AddNode(parent, SAVE_AIRCRAFT_UFOS"UFOs");
2412 for (i = 0; i < MAX_UFOONGEOSCAPE8; i++) {
2413 const aircraft_t *ufo = UFO_GetByIDX(i);
2414 if (!ufo || (ufo->id == NULL__null))
2415 continue;
2416 AIR_SaveAircraftXML(snode, ufo, true);
2417 }
2418
2419 /* Save projectiles. */
2420 node = XML_AddNode(parent, SAVE_AIRCRAFT_PROJECTILES"projectiles");
2421 if (!AIRFIGHT_SaveXML(node))
2422 return false;
2423
2424 return true;
2425}
2426
2427/**
2428 * @brief Loads the weapon slots of an aircraft.
2429 * @param[in] aircraft Pointer to the aircraft.
2430 * @param[out] slot Pointer to the slot where item should be added.
2431 * @param[in] p XML Node structure, where we get the information from
2432 * @param[in] weapon True if the slot is a weapon slot.
2433 * @param[in] max Maximum number of slots for this aircraft that should be loaded.
2434 * @sa B_Load
2435 * @sa B_SaveAircraftSlots
2436 */
2437static void AIR_LoadAircraftSlotsXML (aircraft_t *aircraft, aircraftSlot_t* slot, xmlNode_tmxml_node_t *p, bool weapon, const int max)
2438{
2439 xmlNode_tmxml_node_t *act;
2440 int i;
2441 for (i = 0, act = XML_GetNode(p, SAVE_AIRCRAFT_SLOT"slot"); act && i <= max; act = XML_GetNextNode(act, p, SAVE_AIRCRAFT_SLOT"slot"), i++) {
2442 slot[i].aircraft = aircraft;
2443 AII_LoadOneSlotXML(act, &slot[i], weapon);
2444 }
2445 if (i > max)
2446 Com_Printf("Error: Trying to assign more than max (%d) Aircraft Slots (cur is %d)\n", max, i);
2447
2448}
2449
2450/**
2451 * @brief Loads the route of an aircraft
2452 * @param[in] p XML Node structure, where we get the information from
2453 * @param[out] route Route points of the aircraft
2454 */
2455static bool AIR_LoadRouteXML (xmlNode_tmxml_node_t *p, mapline_t *route)
2456{
2457 xmlNode_tmxml_node_t *actual;
2458 xmlNode_tmxml_node_t *snode;
2459 int count = 0;
2460
2461 snode = XML_GetNode(p, SAVE_AIRCRAFT_ROUTE"route");
2462 if (!snode)
2463 return false;
2464
2465 for (actual = XML_GetPos2(snode, SAVE_AIRCRAFT_ROUTE_POINT"point", route->point[count]); actual && count <= LINE_MAXPTS(64 + 2);
2466 actual = XML_GetNextPos2(actual, snode, SAVE_AIRCRAFT_ROUTE_POINT"point", route->point[++count]))
2467 ;
2468 if (count > LINE_MAXPTS(64 + 2)) {
2469 Com_Printf("AIR_Load: number of points (%i) for UFO route exceed maximum value (%i)\n", count, LINE_MAXPTS(64 + 2));
2470 return false;
2471 }
2472 route->numPoints = count;
2473 route->distance = XML_GetFloat(snode, SAVE_AIRCRAFT_ROUTE_DISTANCE"distance", 0.0);
2474 return true;
2475}
2476
2477/**
2478 * @brief Loads an Aircraft from the savegame
2479 * @param[in] p XML Node structure, where we get the information from
2480 * @param[out] craft Pointer to the aircraft
2481 */
2482static bool AIR_LoadAircraftXML (xmlNode_tmxml_node_t *p, aircraft_t *craft)
2483{
2484 xmlNode_tmxml_node_t *snode;
2485 xmlNode_tmxml_node_t *ssnode;
2486 const char *statusId;
2487 /* vars, if aircraft wasn't found */
2488 int tmpInt;
2489 int l, status;
2490 const char *s = XML_GetString(p, SAVE_AIRCRAFT_ID"id");
2491 const aircraft_t *crafttype = AIR_GetAircraft(s);
2492
2493 /* Copy all datas that don't need to be saved (tpl, hangar,...) */
2494 *craft = *crafttype;
2495
2496 tmpInt = XML_GetInt(p, SAVE_AIRCRAFT_HOMEBASE"homebase", MAX_BASES8);
2497 craft->homebase = (tmpInt != MAX_BASES8) ? B_GetBaseByIDX(tmpInt) : NULL__null;
2498
2499 craft->idx = XML_GetInt(p, SAVE_AIRCRAFT_IDX"idx", -1);
2500 if (craft->idx < 0) {
2501 Com_Printf("Invalid (or no) aircraft index %i\n", craft->idx);
2502 Com_UnregisterConstList(saveAircraftConstants);
2503 return false;
2504 }
2505
2506 Com_RegisterConstList(saveAircraftConstants);
2507
2508 statusId = XML_GetString(p, SAVE_AIRCRAFT_STATUS"status");
2509 if (!Com_GetConstIntFromNamespace(SAVE_AIRCRAFTSTATUS_NAMESPACE"saveAircraftStatus", statusId, &status)) {
2510 Com_Printf("Invalid aircraft status '%s'\n", statusId);
2511 Com_UnregisterConstList(saveAircraftConstants);
2512 return false;
2513 }
2514
2515 craft->status = (aircraftStatus_t)status;
2516 craft->fuel = XML_GetInt(p, SAVE_AIRCRAFT_FUEL"fuel", 0);
2517 craft->damage = XML_GetInt(p, SAVE_AIRCRAFT_DAMAGE"damage", 0);
2518 XML_GetPos3(p, SAVE_AIRCRAFT_POS"pos", craft->pos);
2519
2520 XML_GetPos3(p, SAVE_AIRCRAFT_DIRECTION"direction", craft->direction);
2521 craft->point = XML_GetInt(p, SAVE_AIRCRAFT_POINT"point", 0);
2522 craft->time = XML_GetInt(p, SAVE_AIRCRAFT_TIME"time", 0);
2523
2524 if (!AIR_LoadRouteXML(p, &craft->route)) {
2525 Com_UnregisterConstList(saveAircraftConstants);
2526 return false;
2527 }
2528
2529 s = XML_GetString(p, SAVE_AIRCRAFT_NAME"name");
2530 if (s[0] == '\0')
2531 s = _(craft->defaultName)gettext(craft->defaultName);
2532 Q_strncpyz(craft->name, s, sizeof(craft->name))Q_strncpyzDebug( craft->name, s, sizeof(craft->name), "src/client/cgame/campaign/cp_aircraft.cpp"
, 2532 )
;
2533
2534 s = XML_GetString(p, SAVE_AIRCRAFT_MISSIONID"missionid");
2535 craft->missionID = Mem_PoolStrDup(s, cp_campaignPool, 0)_Mem_PoolStrDup((s),(cp_campaignPool),(0),"src/client/cgame/campaign/cp_aircraft.cpp"
,2535)
;
2536
2537 if (!craft->homebase) {
2538 /* detection id and time */
2539 craft->detectionIdx = XML_GetInt(p, SAVE_AIRCRAFT_DETECTIONIDX"detectionIDX", 0);
2540 XML_GetDate(p, SAVE_AIRCRAFT_LASTSPOTTED_DATE"lastSpottedDate", &craft->lastSpotted.day, &craft->lastSpotted.sec);
2541 }
2542
2543 snode = XML_GetNode(p, SAVE_AIRCRAFT_AIRSTATS"airstats");
2544 for (ssnode = XML_GetNode(snode, SAVE_AIRCRAFT_AIRSTAT"stat"); ssnode; ssnode = XML_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_AIRSTAT"stat")) {
2545 const char *statId = XML_GetString(ssnode, SAVE_AIRCRAFT_AIRSTATID"id");
2546 int idx;
2547
2548 if (!Com_GetConstIntFromNamespace(SAVE_AIRCRAFTSTAT_NAMESPACE"saveAircraftStat", statId, &idx)) {
2549 Com_Printf("Invalid aircraft stat '%s'\n", statId);
2550 Com_UnregisterConstList(saveAircraftConstants);
2551 return false;
2552 }
2553 craft->stats[idx] = XML_GetLong(ssnode, SAVE_AIRCRAFT_VAL"val", 0);
2554#ifdef DEBUG1
2555 /* UFO HP can be < 0 if the UFO has been destroyed */
2556 if (!(!craft->homebase && idx == AIR_STATS_DAMAGE) && craft->stats[idx] < 0)
2557 Com_Printf("Warning: ufo '%s' stats %i: %i is smaller than 0\n", craft->id, idx, craft->stats[idx]);
2558#endif
2559 }
2560
2561 craft->detected = XML_GetBool(p, SAVE_AIRCRAFT_DETECTED"detected", false);
2562 craft->landed = XML_GetBool(p, SAVE_AIRCRAFT_LANDED"landed", false);
2563
2564 tmpInt = XML_GetInt(p, SAVE_AIRCRAFT_AIRCRAFTTARGET"aircraftTarget", -1);
2565 if (tmpInt == -1)
2566 craft->aircraftTarget = NULL__null;
2567 else if (!craft->homebase)
2568 craft->aircraftTarget = AIR_AircraftGetFromIDX(tmpInt);
2569 else
2570 craft->aircraftTarget = ccs.ufos + tmpInt;
2571
2572 /* read equipment slots */
2573 snode = XML_GetNode(p, SAVE_AIRCRAFT_WEAPONS"weapons");
2574 AIR_LoadAircraftSlotsXML(craft, craft->weapons, snode, true, craft->maxWeapons);
2575 snode = XML_GetNode(p, SAVE_AIRCRAFT_SHIELDS"shields");
2576 AIR_LoadAircraftSlotsXML(craft, &craft->shield, snode, false, 1);
2577 snode = XML_GetNode(p, SAVE_AIRCRAFT_ELECTRONICS"electronics");
2578 AIR_LoadAircraftSlotsXML(craft, craft->electronics, snode, false, craft->maxElectronics);
2579
2580 Com_UnregisterConstList(saveAircraftConstants);
2581
2582 /* All other informations are not needed for ufos */
2583 if (!craft->homebase)
2584 return true;
2585
2586 craft->hangar = XML_GetInt(p, SAVE_AIRCRAFT_HANGAR"hangar", 0);
2587
2588 snode = XML_GetNode(p, SAVE_AIRCRAFT_AIRCRAFTTEAM"aircraftTeam");
2589 for (ssnode = XML_GetNode(snode, SAVE_AIRCRAFT_MEMBER"member"); AIR_GetTeamSize(craft) < craft->maxTeamSize && ssnode;
2590 ssnode = XML_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_MEMBER"member")) {
2591 const int ucn = XML_GetInt(ssnode, SAVE_AIRCRAFT_TEAM_UCN"ucn", -1);
2592 if (ucn != -1)
2593 LIST_AddPointer(&craft->acTeam, E_GetEmployeeFromChrUCN(ucn));
2594 }
2595
2596 tmpInt = XML_GetInt(p, SAVE_AIRCRAFT_PILOTUCN"pilotUCN", -1);
2597 /* the employee subsystem is loaded after the base subsystem
2598 * this means, that the pilot pointer is not (really) valid until
2599 * E_Load was called, too */
2600 if (tmpInt != -1)
2601 AIR_SetPilot(craft, E_GetEmployeeFromChrUCN(tmpInt));
2602 else
2603 AIR_SetPilot(craft, NULL__null);
2604
2605 RADAR_Initialise(&craft->radar, RADAR_AIRCRAFTRANGE, RADAR_AIRCRAFTTRACKINGRANGE, 1.0f, false);
2606 RADAR_InitialiseUFOs(&craft->radar);
2607 craft->radar.ufoDetectionProbability = 1;
2608
2609 /* itemcargo */
2610 snode = XML_GetNode(p, SAVE_AIRCRAFT_CARGO"cargo");
2611 for (l = 0, ssnode = XML_GetNode(snode, SAVE_AIRCRAFT_ITEM"item"); l < MAX_CARGO32 && ssnode;
2612 l++, ssnode = XML_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_ITEM"item")) {
2613 const char *const str = XML_GetString(ssnode, SAVE_AIRCRAFT_ITEMID"itemid");
2614 const objDef_t *od = INVSH_GetItemByID(str);
2615
2616 if (!od) {
2617 Com_Printf("AIR_LoadAircraftXML: Could not find aircraftitem '%s'\n", str);
2618 l--;
2619 continue;
2620 }
2621
2622 craft->itemcargo[l].item = od;
2623 craft->itemcargo[l].amount = XML_GetInt(ssnode, SAVE_AIRCRAFT_AMOUNT"amount", 0);
2624 }
2625 craft->itemTypes = l;
2626
2627 /* aliencargo */
2628 snode = XML_GetNode(p, SAVE_AIRCRAFT_ALIENCARGO"alienCargo");
2629 for (l = 0, ssnode = XML_GetNode(snode, SAVE_AIRCRAFT_CARGO"cargo"); l < MAX_CARGO32 && ssnode;
2630 l++, ssnode = XML_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_CARGO"cargo")) {
2631 aliensTmp_t *cargo = AL_GetAircraftAlienCargo(craft)(craft)->alienCargo;
2632 const char *const str = XML_GetString(ssnode, SAVE_AIRCRAFT_TEAMDEFID"teamdefid");
2633
2634 cargo[l].teamDef = Com_GetTeamDefinitionByID(str);
2635 if (!cargo[l].teamDef) {
2636 Com_Printf("AIR_LoadAircraftXML: Could not find teamDef '%s'\n", str);
2637 l--;
2638 continue;
2639 }
2640
2641 cargo[l].amountAlive = XML_GetInt(ssnode, SAVE_AIRCRAFT_ALIVE"alive", 0);
2642 cargo[l].amountDead = XML_GetInt(ssnode, SAVE_AIRCRAFT_DEAD"dead", 0);
2643 }
2644 AL_SetAircraftAlienCargoTypes(craft, l)(craft)->alienCargoTypes = (l);
2645
2646 return true;
2647}
2648
2649/**
2650 * @brief resets aircraftSlots' backreference pointers for aircraft
2651 * @param[in] aircraft Pointer to the aircraft
2652 */
2653static void AIR_CorrectAircraftSlotPointers (aircraft_t *aircraft)
2654{
2655 int i;
2656
2657 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2657, "aircraft") : (void)0)
;
2658
2659 for (i = 0; i < aircraft->maxWeapons; i++) {
2660 aircraft->weapons[i].aircraft = aircraft;
2661 aircraft->weapons[i].base = NULL__null;
2662 aircraft->weapons[i].installation = NULL__null;
2663 }
2664 for (i = 0; i < aircraft->maxElectronics; i++) {
2665 aircraft->electronics[i].aircraft = aircraft;
2666 aircraft->electronics[i].base = NULL__null;
2667 aircraft->electronics[i].installation = NULL__null;
2668 }
2669 aircraft->shield.aircraft = aircraft;
2670 aircraft->shield.base = NULL__null;
2671 aircraft->shield.installation = NULL__null;
2672}
2673
2674bool AIR_LoadXML (xmlNode_tmxml_node_t *parent)
2675{
2676 xmlNode_tmxml_node_t *snode, *ssnode;
2677 xmlNode_tmxml_node_t *projectiles;
2678 int i;
2679
2680 /* load phalanx aircraft */
2681 snode = XML_GetNode(parent, SAVE_AIRCRAFT_PHALANX"aircraft");
2682 for (ssnode = XML_GetNode(snode, SAVE_AIRCRAFT_AIRCRAFT"craft"); ssnode;
2683 ssnode = XML_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_AIRCRAFT"craft")) {
2684 aircraft_t craft;
2685 if (!AIR_LoadAircraftXML(ssnode, &craft))
2686 return false;
2687 assert(craft.homebase)(__builtin_expect(!(craft.homebase), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aircraft.cpp", 2687, "craft.homebase"
) : (void)0)
;
2688 AIR_CorrectAircraftSlotPointers(AIR_Add(craft.homebase, &craft));
2689 }
2690
2691 /* load the ufos on geoscape */
2692 snode = XML_GetNode(parent, SAVE_AIRCRAFT_UFOS"UFOs");
2693
2694 for (i = 0, ssnode = XML_GetNode(snode, SAVE_AIRCRAFT_AIRCRAFT"craft"); i < MAX_UFOONGEOSCAPE8 && ssnode;
2695 ssnode = XML_GetNextNode(ssnode, snode, SAVE_AIRCRAFT_AIRCRAFT"craft"), i++) {
2696 if (!AIR_LoadAircraftXML(ssnode, UFO_GetByIDX(i)))
2697 return false;
2698 ccs.numUFOs++;
2699 }
2700
2701 /* Load projectiles. */
2702 projectiles = XML_GetNode(parent, SAVE_AIRCRAFT_PROJECTILES"projectiles");
2703 if (!AIRFIGHT_LoadXML(projectiles))
2704 return false;
2705
2706 /* check UFOs - backwards */
2707 for (i = ccs.numUFOs - 1; i >= 0; i--) {
2708 aircraft_t *ufo = UFO_GetByIDX(i);
2709 if (ufo->time < 0 || ufo->stats[AIR_STATS_SPEED] <= 0) {
2710 Com_Printf("AIR_Load: Found invalid ufo entry - remove it - time: %i - speed: %i\n",
2711 ufo->time, ufo->stats[AIR_STATS_SPEED]);
2712 UFO_RemoveFromGeoscape(ufo);
2713 }
2714 }
2715
2716 return true;
2717}
2718
2719/**
2720 * @brief Set the mission pointers for all the aircraft after loading a savegame
2721 */
2722static bool AIR_PostLoadInitMissions (void)
2723{
2724 bool success = true;
2725 aircraft_t *prevUfo;
2726 aircraft_t *ufo;
2727
2728 /* PHALANX aircraft */
2729 AIR_Foreach(aircraft)for (bool aircraft__break = false, aircraft__once = true; aircraft__once
; aircraft__once = false) for (linkedList_t const* aircraft__iter
= (ccs.aircraft); ! aircraft__break && aircraft__iter
;) for (aircraft_t* const aircraft = ( aircraft__break = aircraft__once
= true, (aircraft_t*) aircraft__iter->data); aircraft__once
; aircraft__break = aircraft__once = false) if ( aircraft__iter
= aircraft__iter->next, false) {} else
{
2730 if (Q_strnull(aircraft->missionID)((aircraft->missionID) == __null || (aircraft->missionID
)[0] == '\0')
)
2731 continue;
2732 aircraft->mission = CP_GetMissionByID(aircraft->missionID);
2733 if (!aircraft->mission) {
2734 Com_Printf("Aircraft %s (idx: %i) is linked to an invalid mission: %s\n", aircraft->name, aircraft->idx, aircraft->missionID);
2735 if (aircraft->status == AIR_MISSION)
2736 AIR_AircraftReturnToBase(aircraft);
2737 }
2738 Mem_Free(aircraft->missionID)_Mem_Free((aircraft->missionID),"src/client/cgame/campaign/cp_aircraft.cpp"
,2738)
;
2739 aircraft->missionID = NULL__null;
2740 }
2741
2742 /* UFOs */
2743 /**
2744 * @todo UFO_RemoveFromGeoscape call doesn't notify other systems (aircraft, base defences, sam sites, radar)
2745 * about the removal of the UFO. Destroying UFOs should get a dedicated function with all necessary notify-callbacks called
2746 */
2747 prevUfo = NULL__null;
2748 while ((ufo = UFO_GetNext(prevUfo)) != NULL__null) {
2749 if (Q_strnull(ufo->missionID)((ufo->missionID) == __null || (ufo->missionID)[0] == '\0'
)
) {
2750 Com_Printf("Warning: %s (idx: %i) has no mission assigned, removing it\n", ufo->name, ufo->idx);
2751 UFO_RemoveFromGeoscape(ufo);
2752 continue;
2753 }
2754 ufo->mission = CP_GetMissionByID(ufo->missionID);
2755 if (!ufo->mission) {
2756 Com_Printf("Warning: %s (idx: %i) is linked to an invalid mission %s, removing it\n", ufo->name, ufo->idx, ufo->missionID);
2757 UFO_RemoveFromGeoscape(ufo);
2758 continue;
2759 }
2760 ufo->mission->ufo = ufo;
2761 Mem_Free(ufo->missionID)_Mem_Free((ufo->missionID),"src/client/cgame/campaign/cp_aircraft.cpp"
,2761)
;
2762 ufo->missionID = NULL__null;
2763 prevUfo = ufo;
2764 }
2765
2766 return success;
2767}
2768
2769/**
2770 * @brief Actions needs to be done after loading the savegame
2771 * @sa SAV_GameActionsAfterLoad
2772 */
2773bool AIR_PostLoadInit (void)
2774{
2775 return AIR_PostLoadInitMissions();
2776}
2777
2778/**
2779 * @brief Returns true if the current base is able to handle aircraft
2780 * @sa B_BaseInit_f
2781 * @note Hangar must be accessible during base attack to make aircraft lift off and to equip soldiers.
2782 */
2783bool AIR_AircraftAllowed (const base_t* base)
2784{
2785 return B_GetBuildingStatus(base, B_HANGAR) || B_GetBuildingStatus(base, B_SMALL_HANGAR);
2786}
2787
2788/**
2789 * @param aircraft The aircraft to check
2790 * @return @c true if the given aircraft can go on interceptions
2791 */
2792bool AIR_CanIntercept (const aircraft_t *aircraft)
2793{
2794 /* if dependencies of hangar are missing, you can't send aircraft */
2795 if (aircraft->size == AIRCRAFT_SMALL && !B_GetBuildingStatus(aircraft->homebase, B_SMALL_HANGAR))
2796 return false;
2797 if (aircraft->size == AIRCRAFT_LARGE && !B_GetBuildingStatus(aircraft->homebase, B_HANGAR))
2798 return false;
2799
2800 /* we need a pilot to intercept */
2801 if (AIR_GetPilot(aircraft) == NULL__null)
2802 return false;
2803
2804 return true;
2805}
2806
2807/**
2808 * @brief Checks the parsed aircraft for errors
2809 * @return false if there are errors - true otherwise
2810 */
2811bool AIR_ScriptSanityCheck (void)
2812{
2813 int i, j, k, error = 0;
2814 aircraft_t* a;
2815
2816 for (i = 0, a = ccs.aircraftTemplates; i < ccs.numAircraftTemplates; i++, a++) {
2817 if (a->name[0] == '\0') {
2818 error++;
2819 Com_Printf("...... aircraft '%s' has no name\n", a->id);
2820 }
2821 if (!a->defaultName) {
2822 error++;
2823 Com_Printf("...... aircraft '%s' has no defaultName\n", a->id);
2824 }
2825
2826 /* check that every weapons fits slot */
2827 for (j = 0; j < a->maxWeapons - 1; j++)
2828 if (a->weapons[j].item && AII_GetItemWeightBySize(a->weapons[j].item) > a->weapons[j].size) {
2829 error++;
2830 Com_Printf("...... aircraft '%s' has an item (%s) too heavy for its slot\n", a->id, a->weapons[j].item->id);
2831 }
2832
2833 /* check that every slots has a different location for PHALANX aircraft (not needed for UFOs) */
2834 if (a->type != AIRCRAFT_UFO) {
2835 for (j = 0; j < a->maxWeapons - 1; j++) {
2836 const itemPos_t var = a->weapons[j].pos;
2837 for (k = j + 1; k < a->maxWeapons; k++)
2838 if (var == a->weapons[k].pos) {
2839 error++;
2840 Com_Printf("...... aircraft '%s' has 2 weapons slots at the same location\n", a->id);
2841 }
2842 }
2843 for (j = 0; j < a->maxElectronics - 1; j++) {
2844 const itemPos_t var = a->electronics[j].pos;
2845 for (k = j + 1; k < a->maxElectronics; k++)
2846 if (var == a->electronics[k].pos) {
2847 error++;
2848 Com_Printf("...... aircraft '%s' has 2 electronics slots at the same location\n", a->id);
2849 }
2850 }
2851 }
2852 }
2853
2854 return !error;
2855}
2856
2857/**
2858 * @brief Calculates free space in hangars in given base.
2859 * @param[in] aircraftTemplate aircraft in aircraftTemplates list.
2860 * @param[in] base The base to calc the free space in.
2861 * @param[in] used Additional space "used" in hangars (use that when calculating space for more than one aircraft).
2862 * @return Amount of free space in hangars suitable for given aircraft type.
2863 * @note Returns -1 in case of error. Returns 0 if no error but no free space.
2864 */
2865int AIR_CalculateHangarStorage (const aircraft_t *aircraftTemplate, const base_t *base, int used)
2866{
2867 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2867, "base") : (void)0)
;
2868 assert(aircraftTemplate)(__builtin_expect(!(aircraftTemplate), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aircraft.cpp", 2868, "aircraftTemplate"
) : (void)0)
;
2869 assert(aircraftTemplate == aircraftTemplate->tpl)(__builtin_expect(!(aircraftTemplate == aircraftTemplate->
tpl), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 2869, "aircraftTemplate == aircraftTemplate->tpl") : (void
)0)
; /* Make sure it's an aircraft template. */
2870
2871 if (!base->founded) {
2872 return -1;
2873 }
2874
2875 const baseCapacities_t aircraftCapacity = AIR_GetCapacityByAircraftWeight(aircraftTemplate);
2876 /* If this is a small aircraft, we will check space in small hangar.
2877 * If this is a large aircraft, we will check space in big hangar. */
2878 const int freespace = CAP_GetFreeCapacity(base, aircraftCapacity) - used;
2879 return std::max(freespace, 0);
2880}
2881
2882/**
2883 * @brief Removes a soldier from an aircraft.
2884 * @param[in,out] employee The soldier to be removed from the aircraft.
2885 * @param[in,out] aircraft The aircraft to remove the soldier from.
2886 * Use @c NULL to remove the soldier from any aircraft.
2887 * @sa AIR_AddEmployee
2888 */
2889bool AIR_RemoveEmployee (employee_t *employee, aircraft_t *aircraft)
2890{
2891 if (!employee)
2892 return false;
2893
2894 /* If no aircraft is given we search if he is in _any_ aircraft and set
2895 * the aircraft pointer to it. */
2896 if (!aircraft) {
2897 AIR_Foreach(acTemp)for (bool acTemp__break = false, acTemp__once = true; acTemp__once
; acTemp__once = false) for (linkedList_t const* acTemp__iter
= (ccs.aircraft); ! acTemp__break && acTemp__iter;) for
(aircraft_t* const acTemp = ( acTemp__break = acTemp__once =
true, (aircraft_t*) acTemp__iter->data); acTemp__once; acTemp__break
= acTemp__once = false) if ( acTemp__iter = acTemp__iter->
next, false) {} else
{
2898 if (AIR_IsEmployeeInAircraft(employee, acTemp)) {
2899 aircraft = acTemp;
2900 break;
2901 }
2902 }
2903 if (!aircraft)
2904 return false;
2905 }
2906
2907 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_RemoveEmployee: base: %i - aircraft->idx: %i\n",
2908 aircraft->homebase ? aircraft->homebase->idx : -1, aircraft->idx);
2909
2910 if (AIR_GetPilot(aircraft) == employee) {
2911#ifdef DEBUG1
2912 if (employee->type != EMPL_PILOT)
2913 Com_Printf("Warning: pilot of aircraf %i is not a qualified pilot (ucn: %i)\n", aircraft->idx, employee->chr.ucn);
2914#endif
2915 return AIR_SetPilot(aircraft, NULL__null);
2916 }
2917
2918 cgi->INV_DestroyInventory(&employee->chr.i);
2919 return LIST_Remove(&aircraft->acTeam, employee);
2920}
2921
2922/**
2923 * @brief Tells you if an employee is assigned to an aircraft.
2924 * @param[in] employee The employee to search for.
2925 * @param[in] aircraft The aircraft to search the employee in. Use @c NULL to
2926 * check if the soldier is in @b any aircraft.
2927 * @return true if the soldier was found in the aircraft otherwise false.
2928 */
2929const aircraft_t *AIR_IsEmployeeInAircraft (const employee_t *employee, const aircraft_t* aircraft)
2930{
2931 if (!employee)
2932 return NULL__null;
2933
2934 if (employee->transfer)
2935 return NULL__null;
2936
2937 /* If no aircraft is given we search if he is in _any_ aircraft and return true if that's the case. */
2938 if (!aircraft) {
2939 AIR_Foreach(anyAircraft)for (bool anyAircraft__break = false, anyAircraft__once = true
; anyAircraft__once; anyAircraft__once = false) for (linkedList_t
const* anyAircraft__iter = (ccs.aircraft); ! anyAircraft__break
&& anyAircraft__iter;) for (aircraft_t* const anyAircraft
= ( anyAircraft__break = anyAircraft__once = true, (aircraft_t
*) anyAircraft__iter->data); anyAircraft__once; anyAircraft__break
= anyAircraft__once = false) if ( anyAircraft__iter = anyAircraft__iter
->next, false) {} else
{
2940 if (AIR_IsEmployeeInAircraft(employee, anyAircraft))
2941 return anyAircraft;
2942 }
2943 return NULL__null;
2944 }
2945
2946 if (employee->type == EMPL_PILOT) {
2947 if (AIR_GetPilot(aircraft) == employee)
2948 return aircraft;
2949 return NULL__null;
2950 }
2951
2952 if (AIR_IsInAircraftTeam(aircraft, employee))
2953 return aircraft;
2954
2955 return NULL__null;
2956}
2957
2958/**
2959 * @brief Removes all soldiers from an aircraft.
2960 * @param[in,out] aircraft The aircraft to remove the soldiers from.
2961 * @sa AIR_RemoveEmployee
2962 */
2963void AIR_RemoveEmployees (aircraft_t *aircraft)
2964{
2965 if (!aircraft)
2966 return;
2967
2968 LIST_Foreach(aircraft->acTeam, employee_t, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (aircraft->acTeam); ! employee__break && employee__iter
;) for (employee_t* const employee = ( employee__break = employee__once
= true, (employee_t*) employee__iter->data); employee__once
; employee__break = employee__once = false) if ( employee__iter
= employee__iter->next, false) {} else
{
2969 /* use global aircraft index here */
2970 AIR_RemoveEmployee(employee, aircraft);
2971 }
2972
2973 /* Remove pilot */
2974 AIR_SetPilot(aircraft, NULL__null);
2975
2976 if (AIR_GetTeamSize(aircraft) > 0)
2977 Com_Error(ERR_DROP1, "AIR_RemoveEmployees: Error, there went something wrong with soldier-removing from aircraft.");
2978}
2979
2980
2981/**
2982 * @brief Move all the equipment carried by the team on the aircraft into the given equipment
2983 * @param[in] aircraft The craft with the team (and thus equipment) onboard.
2984 * @param[out] ed The equipment definition which will receive all the stuff from the aircraft-team.
2985 */
2986void AIR_MoveEmployeeInventoryIntoStorage (const aircraft_t *aircraft, equipDef_t *ed)
2987{
2988 containerIndex_t container;
2989
2990 if (!aircraft) {
2991 Com_Printf("AIR_MoveEmployeeInventoryIntoStorage: Warning: Called with no aircraft (and thus no carried equipment to add).\n");
2992 return;
2993 }
2994 if (!ed) {
2995 Com_Printf("AIR_MoveEmployeeInventoryIntoStorage: Warning: Called with no equipment definition at add stuff to.\n");
2996 return;
2997 }
2998
2999 if (AIR_GetTeamSize(aircraft) == 0) {
3000 Com_DPrintf(DEBUG_CLIENT0x20, "AIR_MoveEmployeeInventoryIntoStorage: No team to remove equipment from.\n");
3001 return;
3002 }
3003
3004 for (container = 0; container < csi.numIDs; container++) {
3005 LIST_Foreach(aircraft->acTeam, employee_t, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (aircraft->acTeam); ! employee__break && employee__iter
;) for (employee_t* const employee = ( employee__break = employee__once
= true, (employee_t*) employee__iter->data); employee__once
; employee__break = employee__once = false) if ( employee__iter
= employee__iter->next, false) {} else
{
3006 character_t *chr = &employee->chr;
3007 invList_t *ic = CONTAINER(chr, container)((chr)->i.c[(container)]);
3008#if 0
3009 if (INVDEF(container)(&csi.ids[(container)])->temp)
3010 continue;
3011#endif
3012 while (ic) {
3013 const item_t item = ic->item;
3014 const objDef_t *type = item.t;
3015 invList_t *next = ic->next;
3016
3017 ed->numItems[type->idx]++;
3018 if (item.a) {
3019 assert(type->reload)(__builtin_expect(!(type->reload), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aircraft.cpp", 3019, "type->reload"
) : (void)0)
;
3020 assert(item.m)(__builtin_expect(!(item.m), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 3020, "item.m") : (void)0)
;
3021 ed->numItemsLoose[item.m->idx] += item.a;
3022 /* Accumulate loose ammo into clips */
3023 if (ed->numItemsLoose[item.m->idx] >= type->ammo) {
3024 ed->numItemsLoose[item.m->idx] -= type->ammo;
3025 ed->numItems[item.m->idx]++;
3026 }
3027 }
3028 ic = next;
3029 }
3030 }
3031 }
3032}
3033
3034/**
3035 * @brief Assigns a soldier to an aircraft.
3036 * @param[in] employee The employee to be assigned to the aircraft.
3037 * @param[in] aircraft What aircraft to assign employee to.
3038 * @return returns true if a soldier could be assigned to the aircraft.
3039 * @sa AIR_RemoveEmployee
3040 * @sa AIR_AddToAircraftTeam
3041 */
3042bool AIR_AddEmployee (employee_t *employee, aircraft_t *aircraft)
3043{
3044 if (!employee || !aircraft)
3045 return false;
3046
3047 if (AIR_GetTeamSize(aircraft) < aircraft->maxTeamSize) {
3048 /* Check whether the soldier is already on another aircraft */
3049 if (AIR_IsEmployeeInAircraft(employee, NULL__null))
3050 return false;
3051
3052 /* Assign the soldier to the aircraft. */
3053 return AIR_AddToAircraftTeam(aircraft, employee);
3054 }
3055 return false;
3056}
3057
3058/**
3059 * @brief Assigns initial team of soldiers to aircraft
3060 * @param[in,out] aircraft soldiers to add to
3061 */
3062void AIR_AssignInitial (aircraft_t *aircraft)
3063{
3064 int count;
3065 base_t *base;
3066
3067 if (!aircraft) {
3068 Com_Printf("AIR_AssignInitial: No aircraft given\n");
3069 return;
3070 }
3071
3072 base = aircraft->homebase;
3073 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aircraft.cpp"
, 3073, "base") : (void)0)
;
3074
3075 count = 0;
3076 E_Foreach(EMPL_SOLDIER, employee)for (bool employee__break = false, employee__once = true; employee__once
; employee__once = false) for (linkedList_t const* employee__iter
= (ccs.employees[EMPL_SOLDIER]); ! employee__break &&
employee__iter;) for (employee_t* const employee = ( employee__break
= employee__once = true, (employee_t*) employee__iter->data
); employee__once; employee__break = employee__once = false) if
( employee__iter = employee__iter->next, false) {} else
{
3077 if (count >= aircraft->maxTeamSize)
3078 break;
3079 if (employee->baseHired != base)
3080 continue;
3081 if (AIR_AddEmployee(employee, aircraft))
3082 count++;
3083 }
3084}
3085
3086/**
3087 * @brief Init actions for aircraft-subsystem
3088 */
3089void AIR_InitStartup (void)
3090{
3091 AIR_InitCallbacks();
3092#ifdef DEBUG1
3093 Cmd_AddCommand("debug_listaircraftsample", AIR_ListAircraftSamples_f, "Show aircraft parameter on game console");
3094 Cmd_AddCommand("debug_listaircraft", AIR_ListAircraft_f, "Debug function to list all aircraft in all bases");
3095 Cmd_AddCommand("debug_listaircraftidx", AIR_ListCraftIndexes_f, "Debug function to list local/global aircraft indexes");
3096#endif
3097}
3098
3099/**
3100 * @brief Closing actions for aircraft-subsystem
3101 */
3102void AIR_Shutdown (void)
3103{
3104 AIR_Foreach(craft)for (bool craft__break = false, craft__once = true; craft__once
; craft__once = false) for (linkedList_t const* craft__iter =
(ccs.aircraft); ! craft__break && craft__iter;) for (
aircraft_t* const craft = ( craft__break = craft__once = true
, (aircraft_t*) craft__iter->data); craft__once; craft__break
= craft__once = false) if ( craft__iter = craft__iter->next
, false) {} else
{
3105 AIR_ResetAircraftTeam(craft);
3106 }
3107 LIST_Delete(&ccs.aircraft);
3108
3109 AIR_ShutdownCallbacks();
3110#ifdef DEBUG1
3111 Cmd_RemoveCommand("debug_listaircraftsample");
3112 Cmd_RemoveCommand("debug_listaircraft");
3113 Cmd_RemoveCommand("debug_listaircraftidx");
3114#endif
3115}