/** * @brief Puts alien cargo into Alien Containment. * @param[in] aircraft Aircraft transporting cargo to homebase. * @sa B_AircraftReturnedToHomeBase * @sa AL_FillInContainment * @note an event mail about missing breathing tech will be triggered if necessary. */ void AL_AddAliens (aircraft_t* aircraft) { if (!aircraft) return; if (!aircraft->alienCargo) return; if (!aircraft->homebase) { Com_Printf("AL_AddAliens: Aircraft %s (idx: %d) has no base, alienCargo destroyed\n", aircraft->name, aircraft->idx); delete aircraft->alienCargo; aircraft->alienCargo = nullptr; return; } if (aircraft->homebase->alienContainment == nullptr) aircraft->homebase->alienContainment = new AlienContainment(CAP_Get(aircraft->homebase, CAP_ALIENS), nullptr); AlienContainment* cont = aircraft->homebase->alienContainment; if (!cont) return; bool messageSent = false; linkedList_t* cargo = aircraft->alienCargo->list(); LIST_Foreach(cargo, alienCargo_t, item) { const bool lifeSupported = AlienContainment::isLifeSupported(item->teamDef); if (!lifeSupported) { cont->add(item->teamDef, 0, item->alive + item->dead); aircraft->alienCargo->add(item->teamDef, -item->alive, -item->dead); ccs.campaignStats.killedAliens += item->dead + item->alive; if (item->alive > 0) { CP_TriggerEvent(CAPTURED_ALIENS_DIED, nullptr); /* only once */ if (!messageSent) { MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), MSG_DEATH); messageSent = true; } } } else { cont->add(item->teamDef, item->alive, item->dead); aircraft->alienCargo->add(item->teamDef, -item->alive, -item->dead); ccs.campaignStats.killedAliens += item->dead; ccs.campaignStats.capturedAliens += item->alive; if (item->alive > 0) { CP_TriggerEvent(CAPTURED_ALIENS, nullptr); if (!messageSent) { MS_AddNewMessage(_("Notice"), _("You've captured new aliens.")); messageSent = true; } } } } cgi->LIST_Delete(&cargo); }
/** * @brief Actions to execute when a fight is done. * @param[in] campaign The campaign data structure * @param[in] shooter Pointer to the aircraft that fired the projectile. * @param[in] aircraft Pointer to the aircraft which was destroyed (alien or phalanx). * @param[in] phalanxWon qtrue if PHALANX won, qfalse if UFO won. * @note Some of these mission values are redone (and not reloaded) in CP_Load * @note shooter may be NULL * @sa UFO_DestroyAllUFOsOnGeoscape_f * @sa CP_Load * @sa CP_SpawnCrashSiteMission */ void AIRFIGHT_ActionsAfterAirfight (const campaign_t* campaign, aircraft_t *shooter, aircraft_t* aircraft, qboolean phalanxWon) { if (phalanxWon) { const byte *color; assert(aircraft); /* change destination of other projectiles aiming aircraft */ AIRFIGHT_RemoveProjectileAimingAircraft(aircraft); /* now update the projectile for the destroyed aircraft, too */ AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft); /* don't remove ufo from global array: the mission is not over yet * UFO are removed from game only at the end of the mission * (in case we need to know what item to collect e.g.) */ /* get the color value of the map at the crash position */ color = MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN, NULL); /* if this color value is not the value for water ... * and we hit the probability to spawn a crashsite mission */ if (!MapIsWater(color)) { CP_SpawnCrashSiteMission(aircraft); } else { Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ActionsAfterAirfight: zone: %s (%i:%i:%i)\n", MAP_GetTerrainType(color), color[0], color[1], color[2]); MS_AddNewMessage(_("Interception"), _("UFO interception successful -- UFO lost to sea."), qfalse, MSG_STANDARD, NULL); CP_MissionIsOverByUFO(aircraft); } } else { /* change destination of other projectiles aiming aircraft */ AIRFIGHT_RemoveProjectileAimingAircraft(aircraft); /* and now update the projectile pointers (there still might be some in the air * of the current destroyed aircraft) - this is needed not send the aircraft * back to base as soon as the projectiles will hit their target */ AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft); /* notify UFOs that a phalanx aircraft has been destroyed */ UFO_NotifyPhalanxAircraftRemoved(aircraft); if (!MapIsWater(MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN, NULL))) CP_SpawnRescueMission(aircraft, shooter); else { /* Destroy the aircraft and everything onboard - the aircraft pointer * is no longer valid after this point */ AIR_DestroyAircraft(aircraft); } /* Make UFO proceed with its mission, if it has not been already destroyed */ if (shooter) CP_UFOProceedMission(campaign, shooter); MS_AddNewMessage(_("Interception"), _("You've lost the battle"), qfalse, MSG_DEATH, NULL); } }
/** * @brief Sets the title of the base to a cvar to prepare the rename menu. */ static void B_SetBaseTitle_f (void) { int baseCount = B_GetCount(); if (baseCount < MAX_BASES) { char baseName[MAX_VAR]; if (baseCount > 0) { int j; int i = 2; do { j = 0; Com_sprintf(baseName, lengthof(baseName), _("Base #%i"), i); while (j <= baseCount && !Q_streq(baseName, ccs.bases[j].name)) { j++; } } while (i++ <= baseCount && j <= baseCount); } else { Q_strncpyz(baseName, _("Home"), lengthof(baseName)); } cgi->Cvar_Set("mn_base_title", baseName); } else { MS_AddNewMessage(_("Notice"), _("You've reached the base limit.")); cgi->UI_PopWindow(false); /* remove the new base popup */ } }
/** * @brief onDestroy Callback for Antimatter Storage */ static void B_Destroy_AntimaterStorage_f (void) { base_t *base; const float prob = frand(); if (cgi->Cmd_Argc() < 4) { /** note: third parameter not used but we must be sure we have probability parameter */ Com_Printf("Usage: %s <probability> <baseID> <buildingType>\n", cgi->Cmd_Argv(0)); return; } base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(2))); if (!base) return; if (CAP_GetCurrent(base, CAP_ANTIMATTER) <= 0) return; CAP_RemoveAntimatterExceedingCapacity(base); if (base->baseStatus != BASE_WORKING) return; if (prob < atof(cgi->Cmd_Argv(1))) { MS_AddNewMessage(_("Notice"), va(_("%s has been destroyed by an antimatter storage breach."), base->name)); cgi->UI_PopWindow(false); B_Destroy(base); } }
/** * @brief Decide what an attacking aircraft can do. * @param[in] campaign The campaign data structure * @param[in] shooter The aircraft we attack with. * @param[in] target The ufo we are going to attack. * @todo Implement me and display an attack popup. */ void AIRFIGHT_ExecuteActions (const campaign_t* campaign, aircraft_t* shooter, aircraft_t* target) { /* some asserts */ assert(shooter); assert(target); /* Check if the attacking aircraft can shoot */ const int slotIdx = AIRFIGHT_ChooseWeapon(shooter->weapons, shooter->maxWeapons, shooter->pos, target->pos); /* if weapon found that can shoot */ if (slotIdx >= AIRFIGHT_WEAPON_CAN_SHOOT) { aircraftSlot_t* weaponSlot = &shooter->weapons[slotIdx]; const objDef_t* ammo = weaponSlot->ammo; /* shoot */ if (AIRFIGHT_AddProjectile(nullptr, nullptr, shooter, target, weaponSlot)) { /* will we miss the target ? */ const float probability = frand(); Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ExecuteActions: %s - Random probability to hit: %f\n", shooter->name, probability); weaponSlot->delayNextShot = ammo->craftitem.weaponDelay; const float calculatedProbability = AIRFIGHT_ProbabilityToHit(shooter, target, shooter->weapons + slotIdx); Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ExecuteActions: %s - Calculated probability to hit: %f\n", shooter->name, calculatedProbability); if (probability > calculatedProbability) AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1]); if (shooter->type != AIRCRAFT_UFO) { /* Maybe UFO is going to shoot back ? */ UFO_CheckShootBack(campaign, target, shooter); } else { /* an undetected UFO within radar range and firing should become detected */ if (!shooter->detected && RADAR_CheckRadarSensored(shooter->pos)) { /* stop time and notify */ MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is shooting at %s"), target->name)); RADAR_AddDetectedUFOToEveryRadar(shooter); UFO_DetectNewUFO(shooter); } } } } else if (slotIdx == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT) { /* no ammo to fire atm (too far or reloading), pursue target */ if (shooter->type == AIRCRAFT_UFO) { /** @todo This should be calculated only when target destination changes, or when aircraft speed changes. * @sa AIR_GetDestination */ UFO_SendPursuingAircraft(shooter, target); } else AIR_SendAircraftPursuingUFO(shooter, target); } else { /* no ammo left, or no weapon, proceed with mission */ if (shooter->type == AIRCRAFT_UFO) { shooter->aircraftTarget = nullptr; /* reset target */ CP_UFOProceedMission(campaign, shooter); } else { MS_AddNewMessage(_("Notice"), _("Our aircraft has no more ammo left - returning to home base now.")); AIR_AircraftReturnToBase(shooter); } } }
/** * @brief Saves to the quick save slot * @sa SAV_GameQuickLoad_f */ static void SAV_GameQuickSave_f (void) { if (!CP_IsRunning()) return; if (!SAV_QuickSave()) Com_Printf("Could not save the campaign\n"); else MS_AddNewMessage(_("Quicksave"), _("Campaign was successfully saved."), MSG_INFO); }
/** * @brief Saves to the quick save slot */ static void SAV_GameQuickSave_f (void) { if (!CP_IsRunning()) return; if (cgi->CL_OnBattlescape()) return; char* error = nullptr; bool result = SAV_GameSave("slotquick", _("QuickSave"), &error); if (!result) Com_Printf("Error saving the xml game: %s\n", error ? error : ""); else MS_AddNewMessage(_("Quicksave"), _("Campaign was successfully saved."), MSG_INFO); }
/** * @brief Function to start UFO selling process. * @note Command to call this: cp_uforecovery_sell_start. */ static void UR_DialogStartSell_f (void) { int price = -1; const nation_t *nation; int i; if (!ufoRecovery.nation) return; nation = ufoRecovery.nation; i = UR_DialogGetCurrentNationIndex(); price = ufoRecovery.ufoNations[i].price; assert(price >= 0); #if 0 if (ufoRecovery.selectedStorage) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Sold previously recovered %s from %s to nation %s, gained %i credits."), UFO_TypeToName( ufoRecovery.selectedStorage->ufoTemplate->ufotype), ufoRecovery.selectedStorage->base->name, _(nation->name), price); } else #endif { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO sold to nation %s, gained %i credits."), UFO_GetName(ufoRecovery.ufoTemplate), _(nation->name), price); } MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer); CP_UpdateCredits(ccs.credits + price); /* update nation happiness */ for (i = 0; i < ccs.numNations; i++) { nation_t *nat = NAT_GetNationByIDX(i); float ufoHappiness; assert(nat); if (nat == nation) /* nation is happy because it got the UFO */ ufoHappiness = HAPPINESS_UFO_SALE_GAIN; else /* nation is unhappy because it wanted the UFO */ ufoHappiness = HAPPINESS_UFO_SALE_LOSS; NAT_SetHappiness(ccs.curCampaign->minhappiness, nat, nat->stats[0].happiness + ufoHappiness); } /* UFO recovery process is done, disable buttons. */ UR_DialogRecoveryDone(); }
/** * @brief Loads the quick save slot * @sa SAV_GameQuickSave_f */ static void SAV_GameQuickLoad_f (void) { const char *error = NULL; if (cgi->CL_OnBattlescape()) { Com_Printf("Could not load the campaign while you are on the battlefield\n"); return; } if (!SAV_GameLoad("slotquick", &error)) { Cbuf_Execute(); /* wipe outstanding campaign commands */ CP_Popup(_("Error"), "%s\n%s", _("Error loading game."), error ? error : ""); } else { MS_AddNewMessage(_("Campaign loaded"), _("Quicksave campaign was successfully loaded."), MSG_INFO); CP_CheckBaseAttacks(); } }
/** * @brief Base attack mission ends: UFO leave earth. * @note Base attack mission -- Stage 3 * @note UFO attacking this base will be redirected when notify function will be called, don't set new destination here. */ void CP_BaseAttackMissionDestroyBase (mission_t *mission) { base_t *base = mission->data.base; assert(base); /* Base attack is over, alien won */ Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Your base: %s has been destroyed! All employees killed and all equipment destroyed."), base->name); MS_AddNewMessage(_("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL); B_Destroy(base); CL_GameTimeStop(); /* we really don't want to use the fake aircraft anywhere */ MAP_SetMissionAircraft(NULL); /* HACK This hack is only needed until base will be really destroyed * we must recalculate items in storage because of the items we collected on battlefield */ B_UpdateStorageCap(base); base->aircraftCurrent = NULL; base->baseStatus = BASE_WORKING; }
/** * @brief Constructs a new base. * @sa B_NewBase */ static void B_BuildBase_f (void) { const campaign_t* campaign = ccs.curCampaign; if (ccs.mapAction == MA_NEWBASE) ccs.mapAction = MA_NONE; if (ccs.credits - campaign->basecost > 0) { const nation_t* nation; const char* baseName = mn_base_title->string; base_t* base; /* there may be no " in the base name */ if (!Com_IsValidName(baseName)) baseName = _("Base"); base = B_Build(campaign, ccs.newBasePos, baseName); if (!base) cgi->Com_Error(ERR_DROP, "Cannot build base"); CP_UpdateCredits(ccs.credits - campaign->basecost); nation = GEO_GetNation(base->pos); if (nation) Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new base has been built: %s (nation: %s)"), mn_base_title->string, _(nation->name)); else Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new base has been built: %s"), mn_base_title->string); MS_AddNewMessage(_("Base built"), cp_messageBuffer, MSG_CONSTRUCTION); /* First base */ if (ccs.campaignStats.basesBuilt == 1) B_SetUpFirstBase(campaign, base); cgi->Cvar_SetValue("mn_base_count", B_GetCount()); B_SelectBase(base); } else { /** @todo Why is this needed? Also see bug #5401 */ if (GEO_IsRadarOverlayActivated()) GEO_SetOverlay("radar", 0); CP_PopupList(_("Notice"), _("Not enough credits to set up a new base.")); } }
/** * @brief Adds the event mail to the message stack. This message is going to be added to the savegame. */ void CL_EventAddMail (const char *eventMailId) { eventMail_t* eventMail = CL_GetEventMail(eventMailId); if (!eventMail) { Com_Printf("CL_EventAddMail: Could not find eventmail with id '%s'\n", eventMailId); return; } if (eventMail->sent) { return; } if (!eventMail->from || !eventMail->to || !eventMail->subject || !eventMail->body) { Com_Printf("CL_EventAddMail: mail with id '%s' has incomplete data\n", eventMailId); return; } if (!eventMail->date) { dateLong_t date; char dateBuf[MAX_VAR] = ""; CP_DateConvertLong(&ccs.date, &date); Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day); eventMail->date = Mem_PoolStrDup(dateBuf, cp_campaignPool, 0); } eventMail->sent = true; if (!eventMail->skipMessage) { uiMessageListNodeMessage_t *m = MS_AddNewMessage("", va(_("You've got a new mail: %s"), _(eventMail->subject)), MSG_EVENT); if (m) m->eventMail = eventMail; else Com_Printf("CL_EventAddMail: Could not add message with id: %s\n", eventMailId); } UP_OpenEventMail(eventMailId); }
/** * @brief Starts an aircraft or stops the current mission and let the aircraft idle around. */ static void AIM_AircraftStart_f (void) { aircraft_t *aircraft; base_t *base = B_GetCurrentSelectedBase(); if (!base) return; if (!base->aircraftCurrent) { Com_DPrintf(DEBUG_CLIENT, "Error - there is no current aircraft in this base\n"); return; } /* Aircraft cannot start without Command Centre operational. */ if (!B_GetBuildingStatus(base, B_COMMAND)) { CP_Popup(_("Notice"), _("No operational Command Centre in this base.\n\nAircraft can not start.\n")); return; } aircraft = base->aircraftCurrent; /* Aircraft cannot start without a pilot. */ if (!AIR_GetPilot(aircraft)) { CP_Popup(_("Notice"), _("There is no pilot assigned to this aircraft.\n\nAircraft can not start.\n")); return; } if (AIR_IsAircraftInBase(aircraft)) { /* reload its ammunition */ AII_ReloadAircraftWeapons(aircraft); } MS_AddNewMessage(_("Notice"), _("Aircraft started"), qfalse, MSG_STANDARD, NULL); aircraft->status = AIR_IDLE; MAP_SelectAircraft(aircraft); /* Return to geoscape. */ UI_PopWindow(qfalse); UI_PopWindow(qfalse); }
/** * @brief Function to start UFO recovery process. * @note Command to call this: cp_uforecovery_store_start. */ static void UR_DialogStartStore_f (void) { installation_t *installation = nullptr; int idx; int count = 0; date_t date; if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <installationIDX>\n", cgi->Cmd_Argv(0)); return; } idx = atoi(cgi->Cmd_Argv(1)); INS_Foreach(i) { if (i->ufoCapacity.max <= 0 || i->ufoCapacity.max <= i->ufoCapacity.cur) continue; if (count == idx) { installation = i; break; } count++; } if (!installation) return; Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO is being transported to %s."), UFO_GetName(ufoRecovery.ufoTemplate), installation->name); MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer); date = ccs.date; date.day += (int) RECOVERY_DELAY; US_StoreUFO(ufoRecovery.ufoTemplate, installation, date, ufoRecovery.condition); UR_DialogRecoveryDone(); }
/** * @brief Load callback for messages * @param[in] p XML Node structure, where we get the information from * @sa MS_SaveXML * @sa UI_AddNewMessageSound */ bool MS_LoadXML (xmlNode_t* p) { int i; xmlNode_t* n, *sn; n = cgi->XML_GetNode(p, SAVE_MESSAGES_MESSAGES); if (!n) return false; /* we have to set this a little bit higher here, otherwise the samples that are played when adding * a message to the stack would all played a few milliseconds after each other - that doesn't sound * nice */ cgi->S_SetSampleRepeatRate(500); cgi->Com_RegisterConstList(saveMessageConstants); for (sn = cgi->XML_GetNode(n, SAVE_MESSAGES_MESSAGE), i = 0; sn; sn = cgi->XML_GetNextNode(sn, n, SAVE_MESSAGES_MESSAGE), i++) { eventMail_t* mail; const char* type = cgi->XML_GetString(sn, SAVE_MESSAGES_TYPE); int mtype; char title[MAX_VAR]; char text[MAX_MESSAGE_TEXT]; char id[MAX_VAR]; technology_t* tech = nullptr; uiMessageListNodeMessage_t* mess; if (!cgi->Com_GetConstIntFromNamespace(SAVE_MESSAGETYPE_NAMESPACE, type, (int*) &mtype)) { cgi->Com_Printf("Invalid message type '%s'\n", type); continue; } /* can contain high bits due to utf8 */ Q_strncpyz(title, cgi->XML_GetString(sn, SAVE_MESSAGES_TITLE), sizeof(title)); Q_strncpyz(text, cgi->XML_GetString(sn, SAVE_MESSAGES_TEXT), sizeof(text)); if (mtype == MSG_EVENT) { mail = CL_GetEventMail(cgi->XML_GetString(sn, SAVE_MESSAGES_EVENTMAILID)); if (mail) mail->read = cgi->XML_GetBool(sn, SAVE_MESSAGES_EVENTMAILREAD, false); } else mail = nullptr; /* event and not mail means, dynamic mail - we don't save or load them */ if (mtype == MSG_EVENT && !mail) continue; if (mtype == MSG_DEBUG && cgi->Cvar_GetInteger("developer") == 0) continue; Q_strncpyz(id, cgi->XML_GetString(sn, SAVE_MESSAGES_PEDIAID), sizeof(id)); if (id[0] != '\0') tech = RS_GetTechByID(id); if (!tech && (mtype == MSG_RESEARCH_PROPOSAL || mtype == MSG_RESEARCH_FINISHED)) { /** No tech found drop message. */ continue; } mess = MS_AddNewMessage(title, text, (messageType_t)mtype, tech, false, false); mess->eventMail = mail; cgi->XML_GetDate(sn, SAVE_MESSAGES_DATE, &mess->date.day, &mess->date.sec); /* redo timestamp text after setting date */ MS_TimestampedText(mess->timestamp, mess, sizeof(mess->timestamp)); if (mail) { dateLong_t date; char dateBuf[MAX_VAR] = ""; CP_DateConvertLong(&mess->date, &date); Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day); mail->date = cgi->PoolStrDup(dateBuf, cp_campaignPool, 0); } } cgi->Com_UnregisterConstList(saveMessageConstants); /* reset the sample repeat rate */ cgi->S_SetSampleRepeatRate(0); return true; }
/** * @brief Puts alien cargo into Alien Containment. * @param[in] aircraft Aircraft transporting cargo to homebase. * @sa B_AircraftReturnedToHomeBase * @sa AL_FillInContainment * @note an event mail about missing breathing tech will be triggered if necessary. */ void AL_AddAliens (aircraft_t *aircraft) { base_t *toBase; const aliensTmp_t *cargo; int alienCargoTypes; int i; int j; qboolean limit = qfalse; qboolean messageAlreadySet = qfalse; technology_t *breathingTech; qboolean alienBreathing = qfalse; const objDef_t *alienBreathingObjDef; assert(aircraft); toBase = aircraft->homebase; assert(toBase); cargo = AL_GetAircraftAlienCargo(aircraft); alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft); if (alienCargoTypes == 0) return; if (!B_GetBuildingStatus(toBase, B_ALIEN_CONTAINMENT)) { MS_AddNewMessage(_("Notice"), _("You cannot process aliens yet. Alien Containment not ready in this base."), qfalse, MSG_STANDARD, NULL); return; } breathingTech = RS_GetTechByID(BREATHINGAPPARATUS_TECH); if (!breathingTech) Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus tech definition"); alienBreathing = RS_IsResearched_ptr(breathingTech); alienBreathingObjDef = INVSH_GetItemByID(breathingTech->provides); if (!alienBreathingObjDef) Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus item definition"); for (i = 0; i < alienCargoTypes; i++) { for (j = 0; j < ccs.numAliensTD; j++) { assert(toBase->alienscont[j].teamDef); assert(cargo[i].teamDef); if (toBase->alienscont[j].teamDef == cargo[i].teamDef) { toBase->alienscont[j].amountDead += cargo[i].amountDead; /* Add breathing apparatuses to aircraft cargo so that they are processed with other collected items */ AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountDead); if (cargo[i].amountAlive <= 0) continue; if (!alienBreathing && !CHRSH_IsTeamDefRobot(cargo[i].teamDef)) { /* We can not store living (i.e. no robots or dead bodies) aliens without rs_alien_breathing tech */ toBase->alienscont[j].amountDead += cargo[i].amountAlive; /* Add breathing apparatuses as well */ AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountAlive); /* only once */ if (!messageAlreadySet) { MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), qfalse, MSG_DEATH, NULL); messageAlreadySet = qtrue; } if (!ccs.breathingMailSent) { Cmd_ExecuteString("addeventmail alienbreathing"); ccs.breathingMailSent = qtrue; } } else { int k; for (k = 0; k < cargo[i].amountAlive; k++) { /* Check base capacity. */ if (AL_CheckAliveFreeSpace(toBase, NULL, 1)) { AL_ChangeAliveAlienNumber(toBase, &(toBase->alienscont[j]), 1); } else { /* Every exceeding alien is killed * Display a message only when first one is killed */ if (!limit) { toBase->capacities[CAP_ALIENS].cur = toBase->capacities[CAP_ALIENS].max; MS_AddNewMessage(_("Notice"), _("You don't have enough space in Alien Containment. Some aliens got killed."), qfalse, MSG_STANDARD, NULL); limit = qtrue; } /* Just kill aliens which don't fit the limit. */ toBase->alienscont[j].amountDead++; AII_CollectItem(aircraft, alienBreathingObjDef, 1); } } /* only once */ if (!messageAlreadySet) { MS_AddNewMessage(_("Notice"), _("You've captured new aliens."), qfalse, MSG_STANDARD, NULL); messageAlreadySet = qtrue; } } break; } } } for (i = 0; i < ccs.numAliensTD; i++) { aliensCont_t *ac = &toBase->alienscont[i]; technology_t *tech = ac->tech; #ifdef DEBUG if (!tech) Sys_Error("AL_AddAliens: Failed to initialize the tech for '%s'\n", ac->teamDef->name); #endif /* we need this to let RS_Collected_ return true */ if (ac->amountAlive + ac->amountDead > 0) RS_MarkCollected(tech); #ifdef DEBUG /* print all of them */ if (ac->amountAlive > 0) Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens alive: %s amount: %i\n", ac->teamDef->name, ac->amountAlive); if (ac->amountDead > 0) Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens bodies: %s amount: %i\n", ac->teamDef->name, ac->amountDead); #endif } /* we shouldn't have any more aliens on the aircraft after this */ AL_SetAircraftAlienCargoTypes(aircraft, 0); }
/** * @brief Actions to execute when a fight is done. * @param[in] campaign The campaign data structure * @param[in] shooter Pointer to the aircraft that fired the projectile. * @param[in] aircraft Pointer to the aircraft which was destroyed (alien or phalanx). * @param[in] phalanxWon true if PHALANX won, false if UFO won. * @note Some of these mission values are redone (and not reloaded) in CP_Load * @note shooter may be nullptr * @sa UFO_DestroyAllUFOsOnGeoscape_f * @sa CP_Load * @sa CP_SpawnCrashSiteMission */ void AIRFIGHT_ActionsAfterAirfight (const campaign_t* campaign, aircraft_t* shooter, aircraft_t* aircraft, bool phalanxWon) { if (phalanxWon) { const byte* color; assert(aircraft); /* change destination of other projectiles aiming aircraft */ AIRFIGHT_RemoveProjectileAimingAircraft(aircraft); /* now update the projectile for the destroyed aircraft, too */ AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft); /* don't remove ufo from global array: the mission is not over yet * UFO are removed from game only at the end of the mission * (in case we need to know what item to collect e.g.) */ /* get the color value of the map at the crash position */ color = GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr); /* if this color value is not the value for water ... * and we hit the probability to spawn a crashsite mission */ if (!MapIsWater(color)) { CP_SpawnCrashSiteMission(aircraft); } else { Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ActionsAfterAirfight: zone: %s (%i:%i:%i)\n", cgi->csi->terrainDefs.getTerrainName(color), color[0], color[1], color[2]); MS_AddNewMessage(_("Interception"), _("UFO interception successful -- UFO lost to sea.")); CP_MissionIsOverByUFO(aircraft); } /* skill increase (for aircraft only, base defences skip) */ if (shooter) { /* Increase targeting skill of pilot who destroyed UFO. Never more than 70, see AIRFIGHT_ProbabilityToHit() */ shooter->pilot->chr.score.skills[SKILL_TARGETING] += 1; shooter->pilot->chr.score.skills[SKILL_TARGETING] = std::min(shooter->pilot->chr.score.skills[SKILL_TARGETING], 70); /* Increase evasion skill of pilot who destroyed UFO if the aircraft it attacked can carry weapons. * Never more than 70, see AIRFIGHT_ProbabilityToHit() */ if (aircraft->maxWeapons > 0) { shooter->pilot->chr.score.skills[SKILL_EVADING] += 1; shooter->pilot->chr.score.skills[SKILL_EVADING] = std::min(shooter->pilot->chr.score.skills[SKILL_EVADING], 70); } } } else { /* change destination of other projectiles aiming aircraft */ AIRFIGHT_RemoveProjectileAimingAircraft(aircraft); /* and now update the projectile pointers (there still might be some in the air * of the current destroyed aircraft) - this is needed not send the aircraft * back to base as soon as the projectiles will hit their target */ AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft); /* notify UFOs that a phalanx aircraft has been destroyed */ UFO_NotifyPhalanxAircraftRemoved(aircraft); if (!MapIsWater(GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr))) { CP_SpawnRescueMission(aircraft, shooter); } else { /* Destroy the aircraft and everything onboard - the aircraft pointer * is no longer valid after this point */ bool pilotSurvived = false; if (AIR_PilotSurvivedCrash(aircraft)) pilotSurvived = true; AIR_DestroyAircraft(aircraft, pilotSurvived); if (pilotSurvived) MS_AddNewMessage(_("Interception"), _("Pilot ejected from craft"), MSG_STANDARD); else MS_AddNewMessage(_("Interception"), _("Pilot killed in action"), MSG_STANDARD); } /* Make UFO proceed with its mission, if it has not been already destroyed */ if (shooter) CP_UFOProceedMission(campaign, shooter); MS_AddNewMessage(_("Interception"), _("A PHALANX craft has been destroyed"), MSG_DEATH); } }