00001
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "../client.h"
00029 #include "cp_campaign.h"
00030 #include "cp_aliencont_callbacks.h"
00031 #include "save/save_aliencont.h"
00032
00044 static inline int AL_GetCargoIndexForTeamDefintion (const aircraft_t *aircraft, const teamDef_t *teamDef)
00045 {
00046 const aliensTmp_t *cargo = AL_GetAircraftAlienCargo(aircraft);
00047 const int alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft);
00048 int i;
00049
00050 for (i = 0; i < alienCargoTypes; i++, cargo++) {
00051 if (cargo->teamDef == teamDef)
00052 break;
00053 }
00054
00055
00056 assert(i < MAX_CARGO);
00057 return i;
00058 }
00059
00072 qboolean AL_AddAlienTypeToAircraftCargo (aircraft_t *aircraft, const teamDef_t *teamDef, int amount, qboolean dead)
00073 {
00074 aliensTmp_t *cargo = AL_GetAircraftAlienCargo(aircraft);
00075 const int alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft);
00076 const int index = AL_GetCargoIndexForTeamDefintion(aircraft, teamDef);
00077
00078 if (!cargo[index].teamDef)
00079 AL_SetAircraftAlienCargoTypes(aircraft, alienCargoTypes + 1);
00080 cargo[index].teamDef = teamDef;
00081
00082 if (dead)
00083 cargo[index].amountDead += amount;
00084 else
00085 cargo[index].amountAlive += amount;
00086
00087 return qtrue;
00088 }
00089
00100 void AL_FillInContainment (base_t *base)
00101 {
00102 int i, counter = 0;
00103 aliensCont_t *containment;
00104
00105 assert(base);
00106 containment = base->alienscont;
00107
00108 for (i = 0; i < csi.numTeamDefs; i++) {
00109 const teamDef_t *td = &csi.teamDef[i];
00110 if (!CHRSH_IsTeamDefAlien(td))
00111 continue;
00112 if (counter >= MAX_ALIENCONT_CAP)
00113 Com_Error(ERR_DROP, "Overflow in AL_FillInContainment");
00114 containment->teamDef = td;
00115 containment->amountAlive = 0;
00116 containment->amountDead = 0;
00117
00118 containment->tech = ccs.teamDefTechs[td->idx];
00119 if (!containment->tech)
00120 Com_Error(ERR_DROP, "Could not find a valid tech for '%s'\n", td->name);
00121 Com_DPrintf(DEBUG_CLIENT, "AL_FillInContainment: type: %s tech-index: %i\n", td->name, containment->tech->idx);
00122 containment++;
00123 counter++;
00124 }
00125 base->capacities[CAP_ALIENS].cur = 0;
00126 }
00127
00133 const char *AL_AlienTypeToName (int teamDefIdx)
00134 {
00135 if (teamDefIdx < 0 || teamDefIdx >= csi.numTeamDefs) {
00136 Com_Printf("AL_AlienTypeToName: invalid team index %i\n", teamDefIdx);
00137 return NULL;
00138 }
00139 return csi.teamDef[teamDefIdx].name;
00140 }
00141
00148 void AL_CollectingAliens (aircraft_t *aircraft)
00149 {
00150 le_t *le = NULL;
00151
00152 while ((le = LE_GetNextInUse(le))) {
00153 if (LE_IsActor(le) && LE_IsAlien(le)) {
00154 assert(le->teamDef);
00155
00156 if (LE_IsStunned(le))
00157 AL_AddAlienTypeToAircraftCargo(aircraft, le->teamDef, 1, qfalse);
00158 else if (LE_IsDead(le))
00159 AL_AddAlienTypeToAircraftCargo(aircraft, le->teamDef, 1, qtrue);
00160 }
00161 }
00162 }
00163
00171 void AL_AddAliens (aircraft_t *aircraft)
00172 {
00173 base_t *toBase;
00174 const aliensTmp_t *cargo;
00175 int alienCargoTypes;
00176 int i;
00177 int j;
00178 qboolean limit = qfalse;
00179 qboolean messageAlreadySet = qfalse;
00180 technology_t *breathingTech;
00181 qboolean alienBreathing = qfalse;
00182 const objDef_t *alienBreathingObjDef;
00183
00184 assert(aircraft);
00185 toBase = aircraft->homebase;
00186 assert(toBase);
00187
00188 cargo = AL_GetAircraftAlienCargo(aircraft);
00189 alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft);
00190
00191 if (alienCargoTypes == 0)
00192 return;
00193
00194 if (!B_GetBuildingStatus(toBase, B_ALIEN_CONTAINMENT)) {
00195 MS_AddNewMessage(_("Notice"), _("You cannot process aliens yet. Alien Containment not ready in this base."), qfalse, MSG_STANDARD, NULL);
00196 return;
00197 }
00198
00199 breathingTech = RS_GetTechByID(BREATHINGAPPARATUS_TECH);
00200 if (!breathingTech)
00201 Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus tech definition");
00202 alienBreathing = RS_IsResearched_ptr(breathingTech);
00203 alienBreathingObjDef = INVSH_GetItemByID(breathingTech->provides);
00204 if (!alienBreathingObjDef)
00205 Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus item definition");
00206
00207 for (i = 0; i < alienCargoTypes; i++) {
00208 for (j = 0; j < ccs.numAliensTD; j++) {
00209 assert(toBase->alienscont[j].teamDef);
00210 assert(cargo[i].teamDef);
00211
00212 if (toBase->alienscont[j].teamDef == cargo[i].teamDef) {
00213 toBase->alienscont[j].amountDead += cargo[i].amountDead;
00214
00215 AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountDead);
00216 if (cargo[i].amountAlive <= 0)
00217 continue;
00218 if (!alienBreathing && !CHRSH_IsTeamDefRobot(cargo[i].teamDef)) {
00219
00220 toBase->alienscont[j].amountDead += cargo[i].amountAlive;
00221
00222 AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountAlive);
00223
00224 if (!messageAlreadySet) {
00225 MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), qfalse, MSG_DEATH, NULL);
00226 messageAlreadySet = qtrue;
00227 }
00228 if (!ccs.breathingMailSent) {
00229 Cmd_ExecuteString("addeventmail alienbreathing");
00230 ccs.breathingMailSent = qtrue;
00231 }
00232 } else {
00233 int k;
00234
00235 for (k = 0; k < cargo[i].amountAlive; k++) {
00236
00237 if (AL_CheckAliveFreeSpace(toBase, NULL, 1)) {
00238 AL_ChangeAliveAlienNumber(toBase, &(toBase->alienscont[j]), 1);
00239 } else {
00240
00241
00242 if (!limit) {
00243 toBase->capacities[CAP_ALIENS].cur = toBase->capacities[CAP_ALIENS].max;
00244 MS_AddNewMessage(_("Notice"), _("You don't have enough space in Alien Containment. Some aliens got killed."), qfalse, MSG_STANDARD, NULL);
00245 limit = qtrue;
00246 }
00247
00248 toBase->alienscont[j].amountDead++;
00249 AII_CollectItem(aircraft, alienBreathingObjDef, 1);
00250 }
00251 }
00252
00253 if (!messageAlreadySet) {
00254 MS_AddNewMessage(_("Notice"), _("You've captured new aliens."), qfalse, MSG_STANDARD, NULL);
00255 messageAlreadySet = qtrue;
00256 }
00257 }
00258 break;
00259 }
00260 }
00261 }
00262
00263 for (i = 0; i < ccs.numAliensTD; i++) {
00264 technology_t *tech = toBase->alienscont[i].tech;
00265 #ifdef DEBUG
00266 if (!tech)
00267 Sys_Error("AL_AddAliens: Failed to initialize the tech for '%s'\n", toBase->alienscont[i].teamDef->name);
00268 #endif
00269
00270 if (toBase->alienscont[i].amountAlive + toBase->alienscont[i].amountDead > 0)
00271 RS_MarkCollected(tech);
00272 #ifdef DEBUG
00273
00274 if (toBase->alienscont[i].amountAlive > 0)
00275 Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens alive: %s amount: %i\n", toBase->alienscont[i].teamDef->name, toBase->alienscont[i].amountAlive);
00276 if (toBase->alienscont[i].amountDead > 0)
00277 Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens bodies: %s amount: %i\n", toBase->alienscont[i].teamDef->name, toBase->alienscont[i].amountDead);
00278 #endif
00279 }
00280
00281
00282 AL_SetAircraftAlienCargoTypes(aircraft, 0);
00283 }
00284
00296 void AL_RemoveAliens (base_t *base, const teamDef_t *alienType, int amount, const alienCalcType_t action)
00297 {
00298 int j, toremove;
00299 aliensCont_t *containment;
00300
00301 assert(base);
00302 containment = base->alienscont;
00303
00304 switch (action) {
00305 case AL_RESEARCH:
00306 if (!alienType) {
00307 int maxidx = 0;
00308 int maxamount = 0;
00309
00310
00311 while (amount > 0) {
00312
00313 for (j = 0; j < ccs.numAliensTD; j++) {
00314 if (maxamount < containment[j].amountAlive) {
00315 maxamount = containment[j].amountAlive;
00316 maxidx = j;
00317 }
00318 }
00319 if (maxamount == 0) {
00320
00321 Com_Printf("AL_RemoveAliens: unable to find alive aliens\n");
00322 return;
00323 }
00324 if (maxamount == 1) {
00325
00326 AL_ChangeAliveAlienNumber(base, &containment[maxidx], -1);
00327 containment[maxidx].amountDead++;
00328 --amount;
00329 } else {
00330
00331 toremove = maxamount - 1;
00332 if (toremove > amount)
00333 toremove = amount;
00334 AL_ChangeAliveAlienNumber(base, &containment[maxidx], -toremove);
00335 containment[maxidx].amountDead += toremove;
00336 amount -= toremove;
00337 }
00338 }
00339 }
00340 break;
00341 case AL_KILL:
00342
00343 for (j = 0; j < ccs.numAliensTD; j++) {
00344 if (containment[j].amountAlive > 0) {
00345 containment[j].amountDead += containment[j].amountAlive;
00346 AL_ChangeAliveAlienNumber(base, &containment[j], -containment[j].amountAlive);
00347 }
00348 }
00349 break;
00350 case AL_KILLONE:
00351
00352 for (j = 0; j < ccs.numAliensTD; j++) {
00353 assert(containment[j].teamDef);
00354 if (containment[j].teamDef == alienType) {
00355 if (containment[j].amountAlive == 0)
00356 return;
00357
00358
00359 AL_ChangeAliveAlienNumber(base, &containment[j], -1);
00360 containment[j].amountDead++;
00361 break;
00362 }
00363 }
00364 break;
00365 default:
00366 Sys_Error("AL_RemoveAliens: Use AL_AddAliens for action %i", action);
00367 }
00368 }
00369
00370 #ifdef DEBUG
00371
00376 static void AL_AddAliens2 (base_t *base, const teamDef_t *alienType, const qboolean dead)
00377 {
00378 int j;
00379 aliensCont_t *containment;
00380
00381 assert(base);
00382 containment = base->alienscont;
00383
00384 if (dead) {
00385 for (j = 0; j < ccs.numAliensTD; j++) {
00386 assert(containment[j].teamDef);
00387 if (containment[j].teamDef == alienType) {
00388 containment[j].amountDead++;
00389 break;
00390 }
00391 }
00392 } else {
00393
00394 if (!AL_CheckAliveFreeSpace(base, NULL, 1)) {
00395 return;
00396 }
00397 for (j = 0; j < ccs.numAliensTD; j++) {
00398 assert(containment[j].teamDef);
00399 if (containment[j].teamDef == alienType) {
00400 AL_ChangeAliveAlienNumber(base, &containment[j], 1);
00401 break;
00402 }
00403 }
00404 }
00405 }
00406 #endif
00407
00417 static int AL_GetAlienIDX (const teamDef_t *alienType)
00418 {
00419 int i, index;
00420
00421 index = 0;
00422 for (i = 0; i < csi.numTeamDefs; i++) {
00423 if (alienType == &csi.teamDef[i])
00424 return index;
00425 if (CHRSH_IsTeamDefAlien(&csi.teamDef[i]))
00426 index++;
00427 }
00428
00429 Com_Printf("AL_GetAlienIDX: Alien \"%s\" not found!\n", alienType->id);
00430 return -1;
00431 }
00432
00439 int AL_GetAlienGlobalIDX (int idx)
00440 {
00441 int i, counter = 0;
00442
00443 for (i = 0; i < csi.numTeamDefs; i++) {
00444 if (CHRSH_IsTeamDefAlien(&csi.teamDef[i])) {
00445 if (counter == idx)
00446 return i;
00447 counter++;
00448 }
00449 }
00450 Com_Printf("AL_GetAlienGlobalIDX: Alien with AC index %i not found!\n", idx);
00451 return -1;
00452 }
00453
00463 int AL_GetAlienAmount (const teamDef_t *alienType, requirementType_t reqtype, const base_t *base)
00464 {
00465 const aliensCont_t *containment;
00466 int alienTypeIndex;
00467
00468 assert(alienType);
00469 assert(base);
00470 alienTypeIndex = AL_GetAlienIDX(alienType);
00471 assert(alienTypeIndex >= 0);
00472 containment = &base->alienscont[alienTypeIndex];
00473
00474 switch (reqtype) {
00475 case RS_LINK_ALIEN:
00476 return containment->amountAlive;
00477 case RS_LINK_ALIEN_DEAD:
00478 return containment->amountDead;
00479 default:
00480 return containment->amountDead;
00481 }
00482 }
00483
00492 int AL_CountInBase (const base_t *base)
00493 {
00494 int j;
00495 int amount = 0;
00496
00497 assert(base);
00498
00499 for (j = 0; j < ccs.numAliensTD; j++) {
00500 if (base->alienscont[j].teamDef)
00501 amount += base->alienscont[j].amountAlive;
00502 }
00503
00504 return amount;
00505 }
00506
00515 void AL_ChangeAliveAlienNumber (base_t *base, aliensCont_t *containment, int num)
00516 {
00517 assert(base);
00518 assert(containment);
00519
00520
00521 if (!AL_CheckAliveFreeSpace(base, containment, num))
00522 Com_Error(ERR_DROP, "AL_ChangeAliveAlienNumber: Can't add/remove %i live aliens, (capacity: %i/%i, Alien Containment Status: %i)\n",
00523 num, base->capacities[CAP_ALIENS].cur, base->capacities[CAP_ALIENS].max,
00524 B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT));
00525
00526 containment->amountAlive += num;
00527 base->capacities[CAP_ALIENS].cur += num;
00528
00529 #ifdef DEBUG
00530 if (base->capacities[CAP_ALIENS].cur != AL_CountInBase(base))
00531 Com_Printf("AL_ChangeAliveAlienNumber: Wrong capacity in Alien containment: %i instead of %i\n",
00532 base->capacities[CAP_ALIENS].cur, AL_CountInBase(base));
00533 #endif
00534 }
00535
00544 qboolean AL_CheckAliveFreeSpace (const base_t *base, const aliensCont_t *containment, const int num)
00545 {
00546 if (num > 0) {
00547
00548
00549 if (!B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT))
00550 return qfalse;
00551 if (base->capacities[CAP_ALIENS].cur + num > base->capacities[CAP_ALIENS].max)
00552 return qfalse;
00553 } else {
00554
00555
00556 if (base->capacities[CAP_ALIENS].cur + num < 0)
00557 return qfalse;
00558 if (containment && (containment->amountAlive + num < 0))
00559 return qfalse;
00560 }
00561
00562 return qtrue;
00563 }
00564
00577 int AL_CountAll (void)
00578 {
00579 int i, j;
00580 int amount = 0;
00581
00582 for (i = 0; i < MAX_BASES; i++) {
00583 const base_t const *base = B_GetFoundedBaseByIDX(i);
00584 if (!base)
00585 continue;
00586 if (!B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT))
00587 continue;
00588 for (j = 0; j < ccs.numAliensTD; j++) {
00589 if (base->alienscont[j].teamDef)
00590 amount += base->alienscont[j].amountAlive;
00591 }
00592 }
00593 return amount;
00594 }
00595
00601 void AC_KillAll (base_t *base)
00602 {
00603 int i;
00604 qboolean aliens = qfalse;
00605
00606 assert(base);
00607
00608
00609 for (i = 0; i < ccs.numAliensTD; i++) {
00610 if (base->alienscont[i].amountAlive > 0) {
00611 aliens = qtrue;
00612 break;
00613 }
00614 }
00615
00616
00617 if (!aliens)
00618 return;
00619
00620 AL_RemoveAliens(base, NULL, 0, AL_KILL);
00621 }
00622
00623 #ifdef DEBUG
00624
00627 static void AC_AddOne_f (void)
00628 {
00629 const char *alienName;
00630 teamDef_t *alienType;
00631 aliensCont_t *containment;
00632 qboolean updateAlive = qtrue;
00633 int j;
00634 base_t *base = B_GetCurrentSelectedBase();
00635
00636
00637 if (!base)
00638 return;
00639
00640
00641 if (Cmd_Argc() < 2) {
00642 Com_Printf("Usage: %s <alientype> [dead:true|false]\n", Cmd_Argv(0));
00643 return;
00644 }
00645
00646 alienName = Cmd_Argv(1);
00647 alienType = Com_GetTeamDefinitionByID(alienName);
00648
00649 if (!alienType) {
00650 Com_Printf("AC_AddOne_f: Team definition '%s' does not exist.\n", alienName);
00651 return;
00652 }
00653
00654
00655 containment = base->alienscont;
00656 for (j = 0; j < ccs.numAliensTD; j++) {
00657 assert(containment[j].teamDef);
00658 if (containment[j].teamDef == alienType)
00659 break;
00660 }
00661 if (j == ccs.numAliensTD) {
00662 Com_Printf("AC_AddOne_f: Alien Type '%s' does not exist. Available choices are:\n", alienName);
00663 for (j = 0; j < ccs.numAliensTD; j++)
00664 Com_Printf("\t* %s\n", containment[j].teamDef->name);
00665 return;
00666 }
00667
00668 if (Cmd_Argc() == 3)
00669 updateAlive = Com_ParseBoolean(Com_Argv(2));
00670
00671
00672 if (B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT)) {
00673 containment = base->alienscont;
00674 } else {
00675 return;
00676 }
00677
00678
00679 AL_AddAliens2(base, alienType, !updateAlive);
00680 }
00681 #endif
00682
00687 void AC_InitStartup (void)
00688 {
00689
00690 #ifdef DEBUG
00691 Cmd_AddCommand("debug_addalientocont", AC_AddOne_f, "Add one alien of a given type");
00692 #endif
00693 AC_InitCallbacks();
00694 }
00695
00702 qboolean AC_SaveXML (mxml_node_t * parent)
00703 {
00704 mxml_node_t *aliencont;
00705 int i;
00706
00707 aliencont = mxml_AddNode(parent, SAVE_ALIENCONT_ALIENCONT);
00708 mxml_AddBoolValue(aliencont, SAVE_ALIENCONT_BREATHINGMAILSENT, ccs.breathingMailSent);
00709
00710 for (i = 0; i < MAX_BASES; i++) {
00711 base_t *base = B_GetFoundedBaseByIDX(i);
00712 mxml_node_t *node;
00713 int k;
00714
00715 if (!base)
00716 continue;
00717 if (!AC_ContainmentAllowed(base))
00718 continue;
00719
00720 node = mxml_AddNode(aliencont, SAVE_ALIENCONT_CONT);
00721 mxml_AddInt(node, SAVE_ALIENCONT_BASEIDX, i);
00722 for (k = 0; k < MAX_ALIENCONT_CAP && k < ccs.numAliensTD; k++) {
00723 mxml_node_t * snode = mxml_AddNode(node, SAVE_ALIENCONT_ALIEN);
00724
00725 assert(base->alienscont);
00726 assert(base->alienscont[k].teamDef);
00727 assert(base->alienscont[k].teamDef->id);
00728
00729 mxml_AddString(snode, SAVE_ALIENCONT_TEAMID, base->alienscont[k].teamDef->id);
00730 mxml_AddIntValue(snode, SAVE_ALIENCONT_AMOUNTALIVE, base->alienscont[k].amountAlive);
00731 mxml_AddIntValue(snode, SAVE_ALIENCONT_AMOUNTDEAD, base->alienscont[k].amountDead);
00732 }
00733 }
00734
00735 return qtrue;
00736 }
00737
00744 qboolean AC_LoadXML (mxml_node_t * parent)
00745 {
00746 mxml_node_t *aliencont;
00747 mxml_node_t *contNode;
00748 int i;
00749
00750 aliencont = mxml_GetNode(parent, SAVE_ALIENCONT_ALIENCONT);
00751 ccs.breathingMailSent = mxml_GetBool(aliencont, SAVE_ALIENCONT_BREATHINGMAILSENT, qfalse);
00752
00753
00754 for (i = 0; i < MAX_BASES; i++) {
00755 base_t *base = B_GetBaseByIDX(i);
00756
00757 AL_FillInContainment(base);
00758 }
00759
00760 for (contNode = mxml_GetNode(aliencont, SAVE_ALIENCONT_CONT); contNode;
00761 contNode = mxml_GetNextNode(contNode, aliencont, SAVE_ALIENCONT_CONT)) {
00762 int j = mxml_GetInt(contNode, SAVE_ALIENCONT_BASEIDX, MAX_BASES);
00763 base_t *base = B_GetFoundedBaseByIDX(j);
00764 int k;
00765 mxml_node_t *alienNode;
00766
00767 if (!base) {
00768 Com_Printf("AC_LoadXML: Invalid base idx '%i'\n", j);
00769 continue;
00770 }
00771
00772 for (k = 0, alienNode = mxml_GetNode(contNode, SAVE_ALIENCONT_ALIEN); alienNode && k < MAX_ALIENCONT_CAP; alienNode = mxml_GetNextNode(alienNode, contNode, SAVE_ALIENCONT_ALIEN), k++) {
00773 const char *const s = mxml_GetString(alienNode, SAVE_ALIENCONT_TEAMID);
00774
00775 base->alienscont[k].teamDef = Com_GetTeamDefinitionByID(s);
00776 if (base->alienscont[k].teamDef) {
00777 base->alienscont[k].amountAlive = mxml_GetInt(alienNode, SAVE_ALIENCONT_AMOUNTALIVE, 0);
00778 base->alienscont[k].amountDead = mxml_GetInt(alienNode, SAVE_ALIENCONT_AMOUNTDEAD, 0);
00779 }
00780 }
00781 }
00782
00783 return qtrue;
00784 }
00785
00791 qboolean AC_ContainmentAllowed (const base_t* base)
00792 {
00793 if (B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT)) {
00794 return qtrue;
00795 } else {
00796 return qfalse;
00797 }
00798 }
00799