Bug Summary

File:client/cgame/campaign/cp_aliencont.cpp
Location:line 673, column 3
Description:Value stored to 'containment' is never read

Annotated Source Code

1/**
2 * @file
3 * @brief Deals with the Alien Containment stuff.
4 * @note Collecting and managing aliens functions prefix: AL_
5 * @note Alien Containment menu functions prefix: AC_
6 */
7
8/*
9Copyright (C) 2002-2011 UFO: Alien Invasion.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25*/
26
27#include "../../cl_shared.h"
28#include "cp_campaign.h"
29#include "cp_capacity.h"
30#include "cp_aliencont_callbacks.h"
31#include "save/save_aliencont.h"
32
33/**
34 * Collecting aliens functions for aircraft
35 */
36
37/**
38 * @brief Searches an existing index in the alien cargo of an aircraft, or returns the next free index of
39 * the alien cargo if the team definition wasn't found in the current alien cargo.
40 * @param[in] aircraft The aircraft that should have the given team definition in its alien cargo
41 * @param[in] teamDef The team definition that should be searched for
42 * @return The index of the team definition in the alien cargo of the given aircraft
43 */
44static inline int AL_GetCargoIndexForTeamDefinition (const aircraft_t *aircraft, const teamDef_t *teamDef)
45{
46 const aliensTmp_t *cargo = AL_GetAircraftAlienCargo(aircraft)(aircraft)->alienCargo;
47 const int alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft)((aircraft)->alienCargoTypes);
48 int i;
49
50 for (i = 0; i < alienCargoTypes; i++, cargo++) {
51 if (cargo->teamDef == teamDef)
52 break;
53 }
54
55 /* in case teamdef wasn't found, return the next free index */
56 assert(i < MAX_CARGO)(__builtin_expect(!(i < 32), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 56, "i < MAX_CARGO") : (void)0)
;
57 return i;
58}
59
60/**
61 * @brief Adds an alientype to an aircraft cargo
62 * @param[in] aircraft The aircraft that owns the alien cargo to add the alien race to
63 * @param[in] teamDef The team definition of the alien race to add to the alien cargo container of the
64 * given aircraft
65 * @param[in] amount The amount of aliens of the given race (@c teamDef ) that should be added to
66 * the alien cargo
67 * @param[in] dead true for cases where the aliens should be added as dead to the alien cargo - false for
68 * living aliens
69 * @todo Return false for cases where the alien race could not be added to the alien cargo of the aircraft
70 * @returns Currently always true
71 */
72bool AL_AddAlienTypeToAircraftCargo (aircraft_t *aircraft, const teamDef_t *teamDef, int amount, bool dead)
73{
74 aliensTmp_t *cargo = AL_GetAircraftAlienCargo(aircraft)(aircraft)->alienCargo;
75 const int alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft)((aircraft)->alienCargoTypes);
76 const int index = AL_GetCargoIndexForTeamDefinition(aircraft, teamDef);
77 aliensTmp_t *c = &cargo[index];
78
79 if (!c->teamDef)
80 AL_SetAircraftAlienCargoTypes(aircraft, alienCargoTypes + 1)(aircraft)->alienCargoTypes = (alienCargoTypes + 1);
81 c->teamDef = teamDef;
82
83 if (dead)
84 c->amountDead += amount;
85 else
86 c->amountAlive += amount;
87
88 return true;
89}
90
91/**
92 * General Collecting aliens functions
93 */
94
95/**
96 * @brief Prepares Alien Containment - names, states, and zeroed amount.
97 * @param[in] base Pointer to the base with AC.
98 * @sa B_BuildBase
99 * @sa AL_AddAliens
100 */
101void AL_FillInContainment (base_t *base)
102{
103 int i, counter = 0;
104 aliensCont_t *containment;
105
106 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 106, "base") : (void)0)
;
107 containment = base->alienscont;
108
109 for (i = 0; i < csi.numTeamDefs; i++) {
110 const teamDef_t *td = &csi.teamDef[i];
111 if (!CHRSH_IsTeamDefAlien(td))
112 continue;
113 if (counter >= MAX_ALIENCONT_CAP32)
114 Com_Error(ERR_DROP1, "Overflow in AL_FillInContainment");
115 containment->teamDef = td; /* Link to global race index. */
116 containment->amountAlive = 0;
117 containment->amountDead = 0;
118 /* for sanity checking */
119 containment->tech = ccs.teamDefTechs[td->idx];
120 if (!containment->tech)
121 Com_Error(ERR_DROP1, "Could not find a valid tech for '%s'\n", td->name);
122 Com_DPrintf(DEBUG_CLIENT0x20, "AL_FillInContainment: type: %s tech-index: %i\n", td->name, containment->tech->idx);
123 containment++;
124 counter++;
125 }
126 CAP_SetCurrent(base, CAP_ALIENS, 0);
127}
128
129/**
130 * @brief Puts alien cargo into Alien Containment.
131 * @param[in] aircraft Aircraft transporting cargo to homebase.
132 * @sa B_AircraftReturnedToHomeBase
133 * @sa AL_FillInContainment
134 * @note an event mail about missing breathing tech will be triggered if necessary.
135 */
136void AL_AddAliens (aircraft_t *aircraft)
137{
138 base_t *toBase;
139 const aliensTmp_t *cargo;
140 int alienCargoTypes;
141 int i;
142 int j;
143 bool limit = false;
144 bool messageAlreadySet = false;
145 technology_t *breathingTech;
146 bool alienBreathing = false;
147
148 assert(aircraft)(__builtin_expect(!(aircraft), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 148, "aircraft") : (void)0)
;
149 toBase = aircraft->homebase;
150 assert(toBase)(__builtin_expect(!(toBase), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 150, "toBase") : (void)0)
;
151
152 cargo = AL_GetAircraftAlienCargo(aircraft)(aircraft)->alienCargo;
153 alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft)((aircraft)->alienCargoTypes);
154
155 if (alienCargoTypes == 0)
156 return;
157
158 if (!B_GetBuildingStatus(toBase, B_ALIEN_CONTAINMENT)) {
159 MS_AddNewMessage(_("Notice")gettext("Notice"), _("You cannot process aliens yet. Alien Containment not ready in this base.")gettext("You cannot process aliens yet. Alien Containment not ready in this base."
)
);
160 return;
161 }
162
163 breathingTech = RS_GetTechByID(BREATHINGAPPARATUS_TECH"rs_alien_breathing");
164 if (!breathingTech)
165 Com_Error(ERR_DROP1, "AL_AddAliens: Could not get breathing apparatus tech definition");
166 alienBreathing = RS_IsResearched_ptr(breathingTech);
167
168 for (i = 0; i < alienCargoTypes; i++) {
169 for (j = 0; j < ccs.numAliensTD; j++) {
170 aliensCont_t *ac = &toBase->alienscont[j];
171 assert(ac->teamDef)(__builtin_expect(!(ac->teamDef), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 171, "ac->teamDef"
) : (void)0)
;
172 assert(cargo[i].teamDef)(__builtin_expect(!(cargo[i].teamDef), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 172, "cargo[i].teamDef"
) : (void)0)
;
173
174 if (ac->teamDef == cargo[i].teamDef) {
175 const bool isRobot = CHRSH_IsTeamDefRobot(cargo[i].teamDef);
176 ac->amountDead += cargo[i].amountDead;
177
178 if (cargo[i].amountAlive <= 0)
179 continue;
180 if (!alienBreathing && !isRobot) {
181 /* We can not store living (i.e. no robots or dead bodies) aliens without rs_alien_breathing tech */
182 ac->amountDead += cargo[i].amountAlive;
183 /* only once */
184 if (!messageAlreadySet) {
185 MS_AddNewMessage(_("Notice")gettext("Notice"), _("You can't hold live aliens yet. Aliens died.")gettext("You can't hold live aliens yet. Aliens died."), MSG_DEATH);
186 messageAlreadySet = true;
187 }
188 if (!ccs.breathingMailSent) {
189 CL_EventAddMail("alienbreathing");
190 ccs.breathingMailSent = true;
191 }
192 } else {
193 int k;
194
195 for (k = 0; k < cargo[i].amountAlive; k++) {
196 /* Check base capacity. */
197 if (AL_CheckAliveFreeSpace(toBase, NULL__null, 1)) {
198 AL_ChangeAliveAlienNumber(toBase, ac, 1);
199 } else {
200 /* Every exceeding alien is killed
201 * Display a message only when first one is killed */
202 if (!limit) {
203 CAP_SetCurrent(toBase, CAP_ALIENS, CAP_GetMax(toBase, CAP_ALIENS)(toBase)->capacities[(CAP_ALIENS)].max);
204 MS_AddNewMessage(_("Notice")gettext("Notice"), _("You don't have enough space in Alien Containment. Some aliens got killed.")gettext("You don't have enough space in Alien Containment. Some aliens got killed."
)
);
205 limit = true;
206 }
207 /* Just kill aliens which don't fit the limit. */
208 ac->amountDead++;
209 }
210 }
211 /* only once */
212 if (!messageAlreadySet) {
213 MS_AddNewMessage(_("Notice")gettext("Notice"), _("You've captured new aliens.")gettext("You've captured new aliens."));
214 messageAlreadySet = true;
215 }
216 }
217 break;
218 }
219 }
220 }
221
222 for (i = 0; i < ccs.numAliensTD; i++) {
223 aliensCont_t *ac = &toBase->alienscont[i];
224 technology_t *tech = ac->tech;
225#ifdef DEBUG1
226 if (!tech)
227 Sys_Error("AL_AddAliens: Failed to initialize the tech for '%s'\n", ac->teamDef->name);
228#endif
229 /* we need this to let RS_Collected_ return true */
230 if (ac->amountAlive + ac->amountDead > 0)
231 RS_MarkCollected(tech);
232#ifdef DEBUG1
233 /* print all of them */
234 if (ac->amountAlive > 0)
235 Com_DPrintf(DEBUG_CLIENT0x20, "AL_AddAliens alive: %s amount: %i\n", ac->teamDef->name, ac->amountAlive);
236 if (ac->amountDead > 0)
237 Com_DPrintf(DEBUG_CLIENT0x20, "AL_AddAliens bodies: %s amount: %i\n", ac->teamDef->name, ac->amountDead);
238#endif
239 }
240
241 /* we shouldn't have any more aliens on the aircraft after this */
242 AL_SetAircraftAlienCargoTypes(aircraft, 0)(aircraft)->alienCargoTypes = (0);
243}
244
245/**
246 * @brief Removes alien(s) from Alien Containment.
247 * @param[in,out] base Pointer to the base where we will perform action (remove, add, ... aliens).
248 * @param[in] alienType Type of the alien (a teamDef_t pointer)
249 * @param[in] amount Amount of aliens to be removed.
250 * @param[in] action Type of action (see alienCalcType_t).
251 * @sa AC_KillAll_f
252 * @sa AC_KillOne_f
253 * @note Call with NULL name when no matters what type to remove.
254 * @todo integrate this with research system
255 */
256void AL_RemoveAliens (base_t *base, const teamDef_t *alienType, int amount, const alienCalcType_t action)
257{
258 int j, toremove;
259 aliensCont_t *containment;
260
261 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 261, "base") : (void)0)
;
262 containment = base->alienscont;
263
264 switch (action) {
265 case AL_RESEARCH:
266 if (!alienType) {
267 int maxidx = 0;
268 int maxamount = 0; /* amount (of alien type), which is max in Containment) */
269 /* Search for the type of alien, which has max amount
270 * in Alien Containment, then remove (amount). */
271 while (amount > 0) {
272 /* Find the type with maxamount. */
273 for (j = 0; j < ccs.numAliensTD; j++) {
274 const aliensCont_t *ac = &containment[j];
275 if (maxamount < ac->amountAlive) {
276 maxamount = ac->amountAlive;
277 maxidx = j;
278 }
279 }
280 if (maxamount == 0) {
281 /* That should never happen. */
282 Com_Printf("AL_RemoveAliens: unable to find alive aliens\n");
283 return;
284 }
285 if (maxamount == 1) {
286 /* If only one here, just remove. */
287 AL_ChangeAliveAlienNumber(base, &containment[maxidx], -1);
288 containment[maxidx].amountDead++;
289 --amount;
290 } else {
291 /* If more than one, remove the amount. */
292 toremove = maxamount - 1;
293 if (toremove > amount)
294 toremove = amount;
295 AL_ChangeAliveAlienNumber(base, &containment[maxidx], -toremove);
296 containment[maxidx].amountDead += toremove;
297 amount -= toremove;
298 }
299 }
300 }
301 break;
302 case AL_KILL:
303 /* We ignore 2nd and 3rd parameter of AL_RemoveAliens() here. */
304 for (j = 0; j < ccs.numAliensTD; j++) {
305 aliensCont_t *ac = &containment[j];
306 if (ac->amountAlive > 0) {
307 ac->amountDead += ac->amountAlive;
308 AL_ChangeAliveAlienNumber(base, ac, -ac->amountAlive);
309 }
310 }
311 break;
312 case AL_KILLONE:
313 /* We ignore 3rd parameter of AL_RemoveAliens() here. */
314 for (j = 0; j < ccs.numAliensTD; j++) {
315 aliensCont_t *ac = &containment[j];
316 assert(ac->teamDef)(__builtin_expect(!(ac->teamDef), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 316, "ac->teamDef"
) : (void)0)
;
317 if (ac->teamDef == alienType) {
318 if (ac->amountAlive == 0)
319 return;
320 /* We are killing only one here, so we
321 * don't care about amount param. */
322 AL_ChangeAliveAlienNumber(base, ac, -1);
323 ac->amountDead++;
324 break;
325 }
326 }
327 break;
328 default:
329 Sys_Error("AL_RemoveAliens: Use AL_AddAliens for action %i", action);
330 }
331}
332
333#ifdef DEBUG1
334/**
335 * @todo find a better name (or rename the other AL_AddAliens function
336 * @todo use this function more often - the containment[j].amountDead and containment[j].amountAlive counter
337 * are used all over the code
338 */
339static void AL_AddAliens2 (base_t *base, const teamDef_t *alienType, const bool dead)
340{
341 int j;
342 aliensCont_t *containment;
343
344 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 344, "base") : (void)0)
;
345 containment = base->alienscont;
346
347 if (dead) {
348 for (j = 0; j < ccs.numAliensTD; j++) {
349 aliensCont_t *ac = &containment[j];
350 assert(ac->teamDef)(__builtin_expect(!(ac->teamDef), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 350, "ac->teamDef"
) : (void)0)
;
351 if (ac->teamDef == alienType) {
352 ac->amountDead++;
353 break;
354 }
355 }
356 } else {
357 /* We ignore 3rd parameter of AL_RemoveAliens() here: add only 1 alien */
358 if (!AL_CheckAliveFreeSpace(base, NULL__null, 1)) {
359 return; /* stop because we will else exceed the max of aliens */
360 }
361 for (j = 0; j < ccs.numAliensTD; j++) {
362 aliensCont_t *ac = &containment[j];
363 assert(ac->teamDef)(__builtin_expect(!(ac->teamDef), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 363, "ac->teamDef"
) : (void)0)
;
364 if (ac->teamDef == alienType) {
365 AL_ChangeAliveAlienNumber(base, ac, 1);
366 break;
367 }
368 }
369 }
370}
371#endif
372
373/**
374 * @brief Get index of alien.
375 * @param[in] alienType Pointer to alien type.
376 * @return Index of alien in alien containment (so less than @c ccs.numAliensTD)
377 * @note It does NOT return the global team index from @c csi.teamDef array.
378 * That would be @c alienType->idx
379 * @sa RS_AssignTechLinks
380 * @sa AL_GetAlienGlobalIDX
381 */
382static int AL_GetAlienIDX (const teamDef_t *alienType)
383{
384 int i, index;
385
386 index = 0;
387 for (i = 0; i < csi.numTeamDefs; i++) {
388 const teamDef_t *td = &csi.teamDef[i];
389 if (alienType == td)
390 return index;
391 if (CHRSH_IsTeamDefAlien(td))
392 index++;
393 }
394
395 Com_Printf("AL_GetAlienIDX: Alien \"%s\" not found!\n", alienType->id);
396 return -1;
397}
398
399/**
400 * @brief Returns teamdef for global alien idx.
401 * @param[in] alienTeamDefIdx Alien index
402 * @sa AL_GetAlienIDX
403 */
404const teamDef_t* AL_GetAlienTeamDef (int alienTeamDefIdx)
405{
406 int i, counter = 0;
407
408 for (i = 0; i < csi.numTeamDefs; i++) {
409 const teamDef_t *td = &csi.teamDef[i];
410 if (CHRSH_IsTeamDefAlien(td)) {
411 if (counter == alienTeamDefIdx)
412 return td;
413 counter++;
414 }
415 }
416 Com_Printf("AL_GetAlienGlobalIDX: Alien with AC index %i not found!\n", alienTeamDefIdx);
417 return NULL__null;
418}
419
420/**
421 * @brief Get amount of live aliens or alien bodies stored in Containment.
422 * @param[in] alienType The alien type to check for
423 * @param[in] base The base to count in
424 * @param[in] reqtype Requirement type (RS_LINK_ALIEN/RS_LINK_ALIEN_DEAD).
425 * @return Amount of desired alien/body.
426 * @sa RS_RequirementsMet
427 * @sa RS_CheckCollected
428 */
429int AL_GetAlienAmount (const teamDef_t *alienType, requirementType_t reqtype, const base_t *base)
430{
431 const aliensCont_t *containment;
432 int alienTypeIndex;
433
434 assert(alienType)(__builtin_expect(!(alienType), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 434, "alienType") : (void)0)
;
435 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 435, "base") : (void)0)
;
436 alienTypeIndex = AL_GetAlienIDX(alienType);
437 assert(alienTypeIndex >= 0)(__builtin_expect(!(alienTypeIndex >= 0), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_aliencont.cpp", 437,
"alienTypeIndex >= 0") : (void)0)
;
438 containment = &base->alienscont[alienTypeIndex];
439
440 switch (reqtype) {
441 case RS_LINK_ALIEN:
442 return containment->amountAlive;
443 case RS_LINK_ALIEN_DEAD:
444 return containment->amountDead;
445 default:
446 return containment->amountDead;
447 }
448}
449
450/**
451 * @brief Counts live aliens in base.
452 * @param[in] base Pointer to the base
453 * @return amount of all live aliens stored in containment
454 * @note must not return 0 if hasBuilding[B_ALIEN_CONTAINMENT] is false: used to update capacity
455 * @sa AL_ChangeAliveAlienNumber
456 * @sa B_ResetAllStatusAndCapacities_f
457 */
458int AL_CountInBase (const base_t *base)
459{
460 int j;
461 int amount = 0;
462
463 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 463, "base") : (void)0)
;
464
465 for (j = 0; j < ccs.numAliensTD; j++) {
466 if (base->alienscont[j].teamDef)
467 amount += base->alienscont[j].amountAlive;
468 }
469
470 return amount;
471}
472
473/**
474 * @brief Add / Remove live aliens to Alien Containment.
475 * @param[in] base Pointer to the base where Alien Cont. should be checked.
476 * @param[in] containment Pointer to the containment
477 * @param[in] num Number of alien to be added/removed
478 * @pre free space has already been checked
479 * @todo handle containment[j].amountDead++; in case the @c num is negative?
480 */
481void AL_ChangeAliveAlienNumber (base_t *base, aliensCont_t *containment, int num)
482{
483 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 483, "base") : (void)0)
;
484 assert(containment)(__builtin_expect(!(containment), 0) ? __assert_rtn(__func__,
"src/client/cgame/campaign/cp_aliencont.cpp", 484, "containment"
) : (void)0)
;
485
486 /* Just a sanity check -- should never be reached */
487 if (!AL_CheckAliveFreeSpace(base, containment, num))
488 Com_Error(ERR_DROP1, "AL_ChangeAliveAlienNumber: Can't add/remove %i live aliens, (capacity: %i/%i, Alien Containment Status: %i)\n",
489 num, CAP_GetCurrent(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].cur, CAP_GetMax(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].max,
490 B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT));
491
492 containment->amountAlive += num;
493 CAP_AddCurrent(base, CAP_ALIENS, num);
494
495#ifdef DEBUG1
496 if (CAP_GetCurrent(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].cur != AL_CountInBase(base))
497 Com_Printf("AL_ChangeAliveAlienNumber: Wrong capacity in Alien containment: %i instead of %i\n",
498 CAP_GetCurrent(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].cur, AL_CountInBase(base));
499#endif
500}
501
502/**
503 * @brief Remove aliens that exceed containment capacity
504 * @note called on destroying an Alien Containment (from building_ondestroy)
505 * @param[in, out] base Pointer to the base to check
506 */
507void AL_RemoveAliensExceedingCapacity (base_t *base)
508{
509 const int max = CAP_GetMax(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].max;
510 int current = CAP_GetCurrent(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].cur;
511 int i;
512
513 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 513, "base") : (void)0)
;
514 assert(max >= 0)(__builtin_expect(!(max >= 0), 0) ? __assert_rtn(__func__,
"src/client/cgame/campaign/cp_aliencont.cpp", 514, "max >= 0"
) : (void)0)
;
515
516 for (i = 0; i < ccs.numAliensTD; i++) {
517 const int remove = std::min(base->alienscont[i].amountAlive, current - max);
518
519 if (!base->alienscont[i].teamDef)
520 continue;
521
522 /* remove dead aliens if there is no alien containment */
523 if (max == 0)
524 base->alienscont[i].amountDead = 0;
525
526 if (remove > 0) {
527 base->alienscont[i].amountAlive -= remove;
528 CAP_SetCurrent(base, CAP_ALIENS, current - remove);
529 current = CAP_GetCurrent(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].cur;
530 }
531 }
532
533 assert(max >= current)(__builtin_expect(!(max >= current), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 533, "max >= current"
) : (void)0)
;
534}
535
536/**
537 * @brief Check if live aliens can be added/removed to Alien Containment.
538 * @param[in] base Pointer to the base where Alien Cont. should be checked.
539 * @param[in] containment Pointer to the containment (may be @c NULL when adding
540 * aliens or if you don't care about alien type of alien you're removing)
541 * @param[in] num Number of alien to be added/removed
542 * @return true if action may be performed in base
543 */
544bool AL_CheckAliveFreeSpace (const base_t *base, const aliensCont_t *containment, const int num)
545{
546 if (num > 0) {
547 /* We add aliens */
548 /* you need Alien Containment and it's dependencies to handle aliens */
549 if (!B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT))
550 return false;
551 if (CAP_GetFreeCapacity(base, CAP_ALIENS) < num)
552 return false;
553 } else {
554 /* @note don't check building status here.
555 * dependencies may have been destroyed before alien container (B_Destroy) */
556 if (CAP_GetCurrent(base, CAP_ALIENS)(base)->capacities[(CAP_ALIENS)].cur + num < 0)
557 return false;
558 if (containment && (containment->amountAlive + num < 0))
559 return false;
560 }
561
562 return true;
563}
564
565/**
566 * Menu functions
567 */
568
569/**
570 * @brief Counts live aliens in all bases.
571 * @note This should be called whenever you add or remove
572 * @note aliens from alien containment.
573 * @return amount of all live aliens stored in containments
574 * @sa B_AircraftReturnedToHomeBase
575 * @sa AC_Init_f
576 */
577int AL_CountAll (void)
578{
579 int amount = 0;
580 base_t *base = NULL__null;
581
582 while ((base = B_GetNext(base)) != NULL__null) {
583 int j;
584 if (!B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT))
585 continue;
586 for (j = 0; j < ccs.numAliensTD; j++) {
587 const aliensCont_t *ac = &base->alienscont[j];
588 if (ac->teamDef)
589 amount += ac->amountAlive;
590 }
591 }
592 return amount;
593}
594
595/**
596 * @brief Kill all aliens in given base.
597 * @param[in] base The base in which you want to kill all aliens
598 * @sa AC_KillAll_f
599 */
600void AC_KillAll (base_t *base)
601{
602 int i;
603 bool aliens = false;
604
605 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 605, "base") : (void)0)
;
606
607 /* Are there aliens here at all? */
608 for (i = 0; i < ccs.numAliensTD; i++) {
609 if (base->alienscont[i].amountAlive > 0) {
610 aliens = true;
611 break;
612 }
613 }
614
615 /* No aliens, return. */
616 if (!aliens)
617 return;
618
619 AL_RemoveAliens(base, NULL__null, 0, AL_KILL);
620}
621
622#ifdef DEBUG1
623/**
624 * @brief Add a single alien of a given type.
625 */
626static void AC_AddOne_f (void)
627{
628 const char *alienName;
629 const teamDef_t *alienType;
630 aliensCont_t *containment;
631 bool updateAlive = true;
632 int j;
633 base_t *base = B_GetCurrentSelectedBase();
634
635 /* Can be called from everywhere. */
636 if (!base)
637 return;
638
639 /* arg parsing */
640 if (Cmd_Argc() < 2) {
641 Com_Printf("Usage: %s <alientype> [dead:true|false]\n", Cmd_Argv(0));
642 return;
643 }
644
645 alienName = Cmd_Argv(1);
646 alienType = Com_GetTeamDefinitionByID(alienName);
647
648 if (!alienType) {
649 Com_Printf("AC_AddOne_f: Team definition '%s' does not exist.\n", alienName);
650 return;
651 }
652
653 /* Check that alientType exists */
654 containment = base->alienscont;
655 for (j = 0; j < ccs.numAliensTD; j++) {
656 aliensCont_t *ac = &containment[j];
657 assert(ac->teamDef)(__builtin_expect(!(ac->teamDef), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 657, "ac->teamDef"
) : (void)0)
;
658 if (ac->teamDef == alienType)
659 break;
660 }
661 if (j == ccs.numAliensTD) {
662 Com_Printf("AC_AddOne_f: Alien Type '%s' does not exist. Available choices are:\n", alienName);
663 for (j = 0; j < ccs.numAliensTD; j++)
664 Com_Printf("\t* %s\n", containment[j].teamDef->name);
665 return;
666 }
667
668 if (Cmd_Argc() == 3)
669 updateAlive = Com_ParseBoolean(Com_Argv(2));
670
671 /* update alien counter*/
672 if (B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT)) {
673 containment = base->alienscont;
Value stored to 'containment' is never read
674 } else {
675 return;
676 }
677
678 /* call the function that actually changes the persistent datastructure */
679 AL_AddAliens2(base, alienType, !updateAlive);
680}
681#endif
682
683/**
684 * @brief Defines commands and cvars for the alien containment menu(s).
685 * @sa UI_InitStartup
686 */
687void AC_InitStartup (void)
688{
689 /* add commands */
690#ifdef DEBUG1
691 Cmd_AddCommand("debug_addalientocont", AC_AddOne_f, "Add one alien of a given type");
692#endif
693 AC_InitCallbacks();
694}
695
696/**
697 * @brief Savecallback for saving in XML Format
698 * @sa AC_LoadXML
699 * @sa B_SaveXML
700 * @sa SAV_GameSaveXML
701 */
702bool AC_SaveXML (xmlNode_tmxml_node_t * parent)
703{
704 xmlNode_tmxml_node_t *aliencont;
705 base_t *base;
706
707 aliencont = XML_AddNode(parent, SAVE_ALIENCONT_ALIENCONT"alienCont");
708 XML_AddBoolValue(aliencont, SAVE_ALIENCONT_BREATHINGMAILSENT"breathingMailSent", ccs.breathingMailSent);
709
710 base = NULL__null;
711 while ((base = B_GetNext(base)) != NULL__null) {
712 xmlNode_tmxml_node_t *node;
713 int k;
714
715 if (!AC_ContainmentAllowed(base))
716 continue;
717
718 node = XML_AddNode(aliencont, SAVE_ALIENCONT_CONT"cont");
719 XML_AddInt(node, SAVE_ALIENCONT_BASEIDX"baseIDX", base->idx);
720 for (k = 0; k < MAX_ALIENCONT_CAP32 && k < ccs.numAliensTD; k++) {
721 xmlNode_tmxml_node_t * snode = XML_AddNode(node, SAVE_ALIENCONT_ALIEN"alien");
722
723 assert(base->alienscont)(__builtin_expect(!(base->alienscont), 0) ? __assert_rtn(__func__
, "src/client/cgame/campaign/cp_aliencont.cpp", 723, "base->alienscont"
) : (void)0)
;
724 assert(base->alienscont[k].teamDef)(__builtin_expect(!(base->alienscont[k].teamDef), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_aliencont.cpp", 724,
"base->alienscont[k].teamDef") : (void)0)
;
725 assert(base->alienscont[k].teamDef->id)(__builtin_expect(!(base->alienscont[k].teamDef->id), 0
) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_aliencont.cpp"
, 725, "base->alienscont[k].teamDef->id") : (void)0)
;
726
727 XML_AddString(snode, SAVE_ALIENCONT_TEAMID"id", base->alienscont[k].teamDef->id);
728 XML_AddIntValue(snode, SAVE_ALIENCONT_AMOUNTALIVE"amountAlive", base->alienscont[k].amountAlive);
729 XML_AddIntValue(snode, SAVE_ALIENCONT_AMOUNTDEAD"amountDead", base->alienscont[k].amountDead);
730 }
731 }
732
733 return true;
734}
735
736/**
737 * @brief Load callback for savin in XML Format
738 * @sa AC_LoadXML
739 * @sa B_SaveXML
740 * @sa SAV_GameLoadXML
741 */
742bool AC_LoadXML (xmlNode_tmxml_node_t * parent)
743{
744 xmlNode_tmxml_node_t *aliencont;
745 xmlNode_tmxml_node_t *contNode;
746 int i;
747
748 aliencont = XML_GetNode(parent, SAVE_ALIENCONT_ALIENCONT"alienCont");
749 ccs.breathingMailSent = XML_GetBool(aliencont, SAVE_ALIENCONT_BREATHINGMAILSENT"breathingMailSent", false);
750
751 /* Init alienContainers */
752 for (i = 0; i < MAX_BASES8; i++) {
753 base_t *base = B_GetBaseByIDX(i);
754
755 AL_FillInContainment(base);
756 }
757 /* Load data */
758 for (contNode = XML_GetNode(aliencont, SAVE_ALIENCONT_CONT"cont"); contNode;
759 contNode = XML_GetNextNode(contNode, aliencont, SAVE_ALIENCONT_CONT"cont")) {
760 int j = XML_GetInt(contNode, SAVE_ALIENCONT_BASEIDX"baseIDX", MAX_BASES8);
761 base_t *base = B_GetFoundedBaseByIDX(j);
762 int k;
763 xmlNode_tmxml_node_t *alienNode;
764
765 if (!base) {
766 Com_Printf("AC_LoadXML: Invalid base idx '%i'\n", j);
767 continue;
768 }
769
770 for (k = 0, alienNode = XML_GetNode(contNode, SAVE_ALIENCONT_ALIEN"alien"); alienNode && k < MAX_ALIENCONT_CAP32; alienNode = XML_GetNextNode(alienNode, contNode, SAVE_ALIENCONT_ALIEN"alien"), k++) {
771 const char *const s = XML_GetString(alienNode, SAVE_ALIENCONT_TEAMID"id");
772 /* Fill Alien Containment with default values like the tech pointer. */
773 base->alienscont[k].teamDef = Com_GetTeamDefinitionByID(s);
774 if (base->alienscont[k].teamDef) {
775 base->alienscont[k].amountAlive = XML_GetInt(alienNode, SAVE_ALIENCONT_AMOUNTALIVE"amountAlive", 0);
776 base->alienscont[k].amountDead = XML_GetInt(alienNode, SAVE_ALIENCONT_AMOUNTDEAD"amountDead", 0);
777 }
778 }
779 }
780
781 return true;
782}
783
784/**
785 * @brief Returns true if the current base is able to handle captured aliens
786 * @sa B_BaseInit_f
787 * @note Alien cont. must be accessible during base attack to kill aliens.
788 */
789bool AC_ContainmentAllowed (const base_t* base)
790{
791 if (B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT)) {
792 return true;
793 } else {
794 return false;
795 }
796}