/** * @brief Actions to perform when destroying one hangar. * @param[in] base Pointer to the base where hangar is destroyed. * @param[in] capacity Type of hangar capacity: CAP_AIRCRAFT_SMALL or CAP_AIRCRAFT_BIG * @note called when player destroy its building or hangar is destroyed during base attack. * @note These actions will be performed after we actually remove the building. * @pre we checked before calling this function that all parameters are valid. * @pre building is not under construction. * @sa B_BuildingDestroy_f * @todo If player choose to destroy the building, a popup should ask him if he wants to sell aircraft in it. */ void CAP_RemoveAircraftExceedingCapacity (base_t* base, baseCapacities_t capacity) { linkedList_t *awayAircraft = nullptr; int numAwayAircraft; int randomNum; /* destroy aircraft only if there's not enough hangar (hangar is already destroyed) */ if (CAP_GetFreeCapacity(base, capacity) >= 0) return; /* destroy one aircraft (must not be sold: may be destroyed by aliens) */ AIR_ForeachFromBase(aircraft, base) { const int aircraftSize = aircraft->size; switch (aircraftSize) { case AIRCRAFT_SMALL: if (capacity != CAP_AIRCRAFT_SMALL) continue; break; case AIRCRAFT_LARGE: if (capacity != CAP_AIRCRAFT_BIG) continue; break; default: cgi->Com_Error(ERR_DROP, "B_RemoveAircraftExceedingCapacity: Unknown type of aircraft '%i'", aircraftSize); } /* Only aircraft in hangar will be destroyed by hangar destruction */ if (!AIR_IsAircraftInBase(aircraft)) { if (AIR_IsAircraftOnGeoscape(aircraft)) cgi->LIST_AddPointer(&awayAircraft, (void*)aircraft); continue; } /* Remove aircraft and aircraft items, but do not fire employees */ AIR_DeleteAircraft(aircraft); cgi->LIST_Delete(&awayAircraft); return; } numAwayAircraft = cgi->LIST_Count(awayAircraft); if (!numAwayAircraft) return; /* All aircraft are away from base, pick up one and change it's homebase */ randomNum = rand() % numAwayAircraft; if (!CL_DisplayHomebasePopup((aircraft_t*)cgi->LIST_GetByIdx(awayAircraft, randomNum), false)) { aircraft_t *aircraft = (aircraft_t*)cgi->LIST_GetByIdx(awayAircraft, randomNum); /* No base can hold this aircraft */ UFO_NotifyPhalanxAircraftRemoved(aircraft); if (!MapIsWater(GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr))) CP_SpawnRescueMission(aircraft, nullptr); else { /* Destroy the aircraft and everything onboard - the aircraft pointer * is no longer valid after this point */ /* Pilot skills; really kill pilot in this case? */ AIR_DestroyAircraft(aircraft); } } cgi->LIST_Delete(&awayAircraft); }
/** * @brief Update xviInfection value for each nation, using the XVI overlay. * @note should be executed after all daily event that could change XVI overlay */ void CP_UpdateNationXVIInfection (void) { /* No need to update XVI levels if the overlay didn't change */ if (!xviNationInfectionNeedsUpdate) return; /* width in pixel of the XVI overlay */ int width; /* height in pixel of the XVI overlay */ int height; CP_GetXVIMapDimensions(&width, &height); const float heightPerDegree = height / 180.0f; const float widthPerDegree = width / 360.0f; /* parameter used to normalize nation XVI level. * decrease this factor to increase XVI level per nation */ const float AREA_FACTOR = 650.0f; /* area used to normalized XVI infection level for each nation. * depend on overlay size so that if we change resolution of * overlay it doesn't impact nation XIInfection */ const float normalizingArea = width * height / AREA_FACTOR; /* temporary array to store the XVI levels */ float xviInfection[MAX_NATIONS]; /* Initialize array */ OBJZERO(xviInfection); for (int y = 0; y < height; y++) { int sum[MAX_NATIONS]; const byte* previousNationColor; const nation_t* nation; /* current position (in latitude / longitude) */ vec2_t currentPos; OBJZERO(sum); Vector2Set(currentPos, 180.0f, 90.0f - y / heightPerDegree); previousNationColor = GEO_GetColor(currentPos, MAPTYPE_NATIONS, nullptr); nation = GEO_GetNation(currentPos); for (int x = 0; x < width; x++) { const byte* nationColor; currentPos[0] = 180.0f - x / widthPerDegree; nationColor = GEO_GetColor(currentPos, MAPTYPE_NATIONS, nullptr); if (!VectorCompare(nationColor, previousNationColor)) { previousNationColor = nationColor; nation = GEO_GetNation(currentPos); } if (nation) { const int xviLevel = CP_GetXVILevel(x, y); if (xviLevel > 0) sum[nation->idx] += xviLevel; } } /* divide the total XVI infection by the area of a pixel * because pixel are smaller as you go closer from the pole */ for (int nationIdx = 0; nationIdx < ccs.numNations; nationIdx++) xviInfection[nationIdx] += ((float) sum[nationIdx]) / (cos(torad * currentPos[1]) * normalizingArea); } /* copy the new values of XVI infection level into nation array */ for (int nationIdx = 0; nationIdx < ccs.numNations; nationIdx++) { nation_t* nation = NAT_GetNationByIDX(nationIdx); nation->stats[0].xviInfection = ceil(xviInfection[nation->idx]); } xviNationInfectionNeedsUpdate = false; }
/** * @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); } }