File: | client/cgame/campaign/cp_aircraft.cpp |
Location: | line 2022, column 2 |
Description: | The left operand of '<=' is a garbage value |
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 | /* | ||||
11 | Copyright (C) 2002-2012 UFO: Alien Invasion. | ||||
12 | |||||
13 | This program is free software; you can redistribute it and/or | ||||
14 | modify it under the terms of the GNU General Public License | ||||
15 | as published by the Free Software Foundation; either version 2 | ||||
16 | of the License, or (at your option) any later version. | ||||
17 | |||||
18 | This program is distributed in the hope that it will be useful, | ||||
19 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
21 | |||||
22 | See the GNU General Public License for more details. | ||||
23 | |||||
24 | You should have received a copy of the GNU General Public License | ||||
25 | along with this program; if not, write to the Free Software | ||||
26 | Foundation, 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 | */ | ||||
47 | aircraft_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 | */ | ||||
62 | bool 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 | */ | ||||
71 | int 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 | */ | ||||
92 | static 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 | */ | ||||
124 | void 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 | */ | ||||
144 | void 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 | |||||
251 | static 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 | */ | ||||
259 | static 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 | */ | ||||
279 | void 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 | |||||
303 | static 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 | */ | ||||
313 | static 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 | */ | ||||
356 | void 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 | */ | ||||
410 | const 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 | */ | ||||
454 | bool 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 | */ | ||||
468 | bool 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 | */ | ||||
496 | int 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 | */ | ||||
512 | int 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 | */ | ||||
526 | const 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 | */ | ||||
544 | int 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 | */ | ||||
563 | int 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 | */ | ||||
575 | int 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 | */ | ||||
587 | bool 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 | */ | ||||
613 | bool 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 | */ | ||||
631 | void 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 | */ | ||||
650 | int 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 | */ | ||||
677 | aircraft_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 | */ | ||||
697 | const 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 | */ | ||||
716 | const 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 | */ | ||||
731 | static 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 | */ | ||||
751 | aircraft_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 | */ | ||||
765 | bool 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 | */ | ||||
776 | aircraft_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 | */ | ||||
824 | baseCapacities_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 | */ | ||||
840 | static 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 | |||||
867 | const 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 | */ | ||||
895 | static 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 | */ | ||||
926 | bool 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 | */ | ||||
990 | void 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 | */ | ||||
1048 | void 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 | */ | ||||
1080 | bool 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 | |||||
1125 | static 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 | |||||
1164 | static 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 | */ | ||||
1222 | void 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 | */ | ||||
1283 | aircraft_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 | */ | ||||
1301 | bool 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 | */ | ||||
1347 | static 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 | */ | ||||
1363 | static 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 */ | ||||
1377 | static 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. */ | ||||
1390 | static 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 | */ | ||||
1416 | void 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 | ||||
1690 | void 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 | */ | ||||
1701 | void 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 | /*=============================================== | ||||
1727 | Aircraft 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 | */ | ||||
1735 | void 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 | */ | ||||
1748 | void 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 | */ | ||||
1790 | void 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 | */ | ||||
1808 | static 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 | */ | ||||
1824 | static 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 | */ | ||||
1840 | static 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 | */ | ||||
1945 | void 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;;) { | ||||
| |||||
1991 | a = AIR_GetDestinationFindRoot(c, B, speedRatio, a); | ||||
1992 | |||||
1993 | if (a < 0.) { | ||||
| |||||
1994 | /* we couldn't find a root on the whole range */ | ||||
1995 | break; | ||||
| |||||
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.) { | ||||
| |||||
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); | ||||
| |||||
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 | */ | ||||
2030 | bool AIR_SendAircraftPursuingUFO (aircraft_t* aircraft, aircraft_t* ufo) | ||||
2031 | { | ||||
2032 | vec2_t dest; | ||||
2033 | |||||
2034 | if (!aircraft) | ||||
| |||||
2035 | return false; | ||||
2036 | |||||
2037 | /* if aircraft was in base */ | ||||
2038 | if (AIR_IsAircraftInBase(aircraft)) { | ||||
| |||||
2039 | /* reload its ammunition */ | ||||
2040 | AII_ReloadAircraftWeapons(aircraft); | ||||
2041 | } | ||||
2042 | |||||
2043 | AIR_GetDestinationWhilePursuing(aircraft, ufo, &dest); | ||||
| |||||
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 | /*============================================ | ||||
2066 | Aircraft 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 | */ | ||||
2073 | void 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 | */ | ||||
2084 | bool 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 | */ | ||||
2106 | bool 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 | */ | ||||
2118 | int 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 | */ | ||||
2132 | bool 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 | */ | ||||
2147 | employee_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 | */ | ||||
2162 | void 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 | */ | ||||
2176 | void 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 | */ | ||||
2193 | int 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 | */ | ||||
2235 | static 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 | */ | ||||
2257 | static 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 | */ | ||||
2273 | static 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 | */ | ||||
2399 | bool 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 | */ | ||||
2437 | static 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 | */ | ||||
2455 | static 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 | */ | ||||
2482 | static 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 | */ | ||||
2653 | static 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 | |||||
2674 | bool 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 | */ | ||||
2722 | static 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 | */ | ||||
2773 | bool 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 | */ | ||||
2783 | bool 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 | */ | ||||
2792 | bool 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 | */ | ||||
2811 | bool 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 | */ | ||||
2865 | int 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 | */ | ||||
2889 | bool 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 | */ | ||||
2929 | const 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 | */ | ||||
2963 | void 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 | */ | ||||
2986 | void 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 | */ | ||||
3042 | bool 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 | */ | ||||
3062 | void 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 | */ | ||||
3089 | void 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 | */ | ||||
3102 | void 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 | } |