/** * @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 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; }