/** * @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 Check if the ufo can shoot back at phalanx aircraft * @param[in] campaign The campaign data structure * @param[in,out] ufo The ufo to check the shotting for * @param[in,out] phalanxAircraft The possible target */ void UFO_CheckShootBack (const campaign_t* campaign, aircraft_t *ufo, aircraft_t* phalanxAircraft) { /* check if the ufo is already attacking an aircraft */ if (ufo->aircraftTarget) { /* check if the target flee in a base */ if (AIR_IsAircraftOnGeoscape(ufo->aircraftTarget)) AIRFIGHT_ExecuteActions(campaign, ufo, ufo->aircraftTarget); else { ufo->aircraftTarget = NULL; CP_UFOProceedMission(campaign, ufo); } } else { /* check that aircraft is flying */ if (AIR_IsAircraftOnGeoscape(phalanxAircraft)) UFO_SendPursuingAircraft(ufo, phalanxAircraft); } }
/** * @brief Check if the ufo can shoot at a PHALANX aircraft */ static void UFO_SearchAircraftTarget (const campaign_t* campaign, aircraft_t *ufo) { float distance = 999999.; /* UFO never try to attack a PHALANX aircraft except if they came on earth in that aim */ if (ufo->mission->stage != STAGE_INTERCEPT) { /* Check if UFO is defending itself */ if (ufo->aircraftTarget) UFO_CheckShootBack(campaign, ufo, ufo->aircraftTarget); return; } /* check if the ufo is already attacking an aircraft */ if (ufo->aircraftTarget) { /* check if the target disappeared from geoscape (fled in a base) */ if (AIR_IsAircraftOnGeoscape(ufo->aircraftTarget)) AIRFIGHT_ExecuteActions(campaign, ufo, ufo->aircraftTarget); else ufo->aircraftTarget = NULL; return; } ufo->status = AIR_TRANSIT; AIR_Foreach(phalanxAircraft) { /* check that aircraft is flying */ if (AIR_IsAircraftOnGeoscape(phalanxAircraft)) { /* get the distance from ufo to aircraft */ const float dist = GetDistanceOnGlobe(ufo->pos, phalanxAircraft->pos); /* check out of reach */ if (dist > MAX_DETECTING_RANGE) continue; /* choose the nearest target */ if (dist < distance) { distance = dist; if (UFO_SendPursuingAircraft(ufo, phalanxAircraft) && UFO_IsUFOSeenOnGeoscape(ufo)) { /* stop time and notify */ MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is flying toward %s"), phalanxAircraft->name), qfalse, MSG_STANDARD, NULL); /** @todo present a popup with possible orders like: return to base, attack the ufo, try to flee the rockets */ } } } } }
/** * @brief Check events for UFOs: Appears or disappears on radars * @return qtrue if any new ufo was detected during this iteration, qfalse otherwise */ qboolean UFO_CampaignCheckEvents (void) { qboolean newDetection; aircraft_t *ufo; newDetection = qfalse; /* For each ufo in geoscape */ ufo = NULL; while ((ufo = UFO_GetNext(ufo)) != NULL) { char detectedBy[MAX_VAR] = ""; float minDistance = -1; /* detected tells us whether or not a UFO is detected NOW, whereas ufo->detected tells * us whether or not the UFO was detected PREVIOUSLY. */ qboolean detected = qfalse; base_t *base; /* don't update UFO status id UFO is landed or crashed */ if (ufo->landed) continue; /* note: We can't exit these loops as soon as we found the UFO detected * RADAR_CheckUFOSensored registers the UFO in every radars' detection list * which detect it */ /* Check if UFO is detected by an aircraft */ AIR_Foreach(aircraft) { if (!AIR_IsAircraftOnGeoscape(aircraft)) continue; /* maybe the ufo is already detected, don't reset it */ if (RADAR_CheckUFOSensored(&aircraft->radar, aircraft->pos, ufo, detected | ufo->detected)) { const int distance = GetDistanceOnGlobe(aircraft->pos, ufo->pos); detected = qtrue; if (minDistance < 0 || minDistance > distance) { minDistance = distance; Q_strncpyz(detectedBy, aircraft->name, sizeof(detectedBy)); } } } /* Check if UFO is detected by a base */ base = NULL; while ((base = B_GetNext(base)) != NULL) { if (!B_GetBuildingStatus(base, B_POWER)) continue; /* maybe the ufo is already detected, don't reset it */ if (RADAR_CheckUFOSensored(&base->radar, base->pos, ufo, detected | ufo->detected)) { const int distance = GetDistanceOnGlobe(base->pos, ufo->pos); detected = qtrue; if (minDistance < 0 || minDistance > distance) { minDistance = distance; Q_strncpyz(detectedBy, base->name, sizeof(detectedBy)); } } } /* Check if UFO is detected by a radartower */ INS_Foreach(installation) { /* maybe the ufo is already detected, don't reset it */ if (RADAR_CheckUFOSensored(&installation->radar, installation->pos, ufo, detected | ufo->detected)) { const int distance = GetDistanceOnGlobe(installation->pos, ufo->pos); detected = qtrue; if (minDistance < 0 || minDistance > distance) { minDistance = distance; Q_strncpyz(detectedBy, installation->name, sizeof(detectedBy)); } } } /* Check if ufo appears or disappears on radar */ if (detected != ufo->detected) { if (detected) { /* if UFO is aiming a PHALANX aircraft, warn player */ if (ufo->aircraftTarget) { /* stop time and notify */ MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is flying toward %s"), ufo->aircraftTarget->name), qfalse, MSG_STANDARD, NULL); /** @todo present a popup with possible orders like: return to base, attack the ufo, try to flee the rockets * @sa UFO_SearchAircraftTarget */ } else { MSO_CheckAddNewMessage(NT_UFO_SPOTTED, _("Notice"), va(_("Our radar detected a new UFO near %s"), detectedBy), qfalse, MSG_UFOSPOTTED, NULL); } newDetection = qtrue; UFO_DetectNewUFO(ufo); } else if (!detected) { MSO_CheckAddNewMessage(NT_UFO_SIGNAL_LOST, _("Notice"), _("Our radar has lost the tracking on a UFO"), qfalse, MSG_UFOLOST, NULL); /* Make this UFO undetected */ ufo->detected = qfalse; /* Notify that ufo disappeared */ AIR_AircraftsUFODisappear(ufo); MAP_NotifyUFODisappear(ufo); /* Deactivate Radar overlay */ RADAR_DeactivateRadarOverlay(); } } } return newDetection; }
/** * @brief Check if the ufo can shoot at a PHALANX aircraft and whether it should follow another ufo */ static void UFO_SearchAircraftTarget (const campaign_t* campaign, aircraft_t* ufo, float maxDetectionRange = MAX_DETECTING_RANGE) { float distance = 999999.; /* UFO never try to attack a PHALANX aircraft except if they came on earth in that aim */ if (ufo->mission->stage != STAGE_INTERCEPT) { /* Check if UFO is defending itself */ if (ufo->aircraftTarget) UFO_CheckShootBack(campaign, ufo, ufo->aircraftTarget); return; } /* check if the ufo is already attacking an aircraft */ if (ufo->aircraftTarget) { /* check if the target disappeared from geoscape (fled in a base) */ if (AIR_IsAircraftOnGeoscape(ufo->aircraftTarget)) AIRFIGHT_ExecuteActions(campaign, ufo, ufo->aircraftTarget); else ufo->aircraftTarget = nullptr; return; } ufo->status = AIR_TRANSIT; AIR_Foreach(phalanxAircraft) { /* check that aircraft is flying */ if (AIR_IsAircraftOnGeoscape(phalanxAircraft)) { /* get the distance from ufo to aircraft */ const float dist = GetDistanceOnGlobe(ufo->pos, phalanxAircraft->pos); /* check out of reach */ if (dist > maxDetectionRange) continue; /* choose the nearest target */ if (dist < distance) { distance = dist; if (UFO_SendPursuingAircraft(ufo, phalanxAircraft) && UFO_IsUFOSeenOnGeoscape(ufo)) { /* stop time and notify */ MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is flying toward %s"), phalanxAircraft->name)); /** @todo present a popup with possible orders like: return to base, attack the ufo, try to flee the rockets */ return; } } } } /* if this ufo is a leader, it does not try to search another one */ if (ufo->leader) return; aircraft_t* otherUFO = nullptr; const float polarCoordinatesOffset = 1.0f; while ((otherUFO = UFO_GetNextOnGeoscape(otherUFO)) != nullptr) { if (otherUFO == ufo) continue; if (otherUFO->leader) { vec2_t dest; AIR_GetDestinationWhilePursuing(ufo, otherUFO, dest); dest[0] += polarCoordinatesOffset; dest[1] += polarCoordinatesOffset; GEO_CalcLine(ufo->pos, dest, &ufo->route); ufo->time = 0; ufo->point = 0; break; } } }