/** * @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 Set all projectile aiming a given aircraft to an idle destination. * @param[in] aircraft Pointer to the aimed aircraft. * @note This function is called when @c aircraft is destroyed. * @sa AIRFIGHT_ActionsAfterAirfight */ void AIRFIGHT_RemoveProjectileAimingAircraft (const aircraft_t * aircraft) { aircraftProjectile_t *projectile; int idx = 0; if (!aircraft) return; for (projectile = ccs.projectiles; idx < ccs.numProjectiles; projectile++, idx++) { if (projectile->aimedAircraft == aircraft) AIRFIGHT_MissTarget(projectile, qtrue); } }
/** * @brief Check if one type of battery (missile or laser) can shoot now. * @param[in] installation Pointer to the firing intallation. * @param[in] weapons The installation weapon to check and fire. */ static void AIRFIGHT_InstallationShoot (const installation_t *installation, baseWeapon_t *weapons, int maxWeapons) { int i, test; float distance; for (i = 0; i < maxWeapons; i++) { aircraft_t *target = weapons[i].target; aircraftSlot_t *slot = &(weapons[i].slot); /* if no target, can't shoot */ if (!target) continue; /* If the weapon is not ready in base, can't shoot. */ if (slot->installationTime > 0) continue; /* if weapon is reloading, can't shoot */ if (slot->delayNextShot > 0) continue; /* check that the ufo is still visible */ if (!UFO_IsUFOSeenOnGeoscape(target)) { weapons[i].target = NULL; continue; } /* Check if we can still fire on this target. */ distance = GetDistanceOnGlobe(installation->pos, target->pos); test = AIRFIGHT_CheckWeapon(slot, distance); /* weapon unable to shoot, reset target */ if (test == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) { weapons[i].target = NULL; continue; } /* we can't shoot with this weapon atm, wait to see if UFO comes closer */ else if (test == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT) continue; /* target is too far, wait to see if UFO comes closer */ else if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE]) continue; /* shoot */ if (AIRFIGHT_AddProjectile(NULL, installation, NULL, target, slot)) { slot->delayNextShot = slot->ammo->craftitem.weaponDelay; /* will we miss the target ? */ if (frand() > AIRFIGHT_ProbabilityToHit(NULL, target, slot)) AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1], qfalse); } } }
/** * @brief Set all projectile aiming a given aircraft to an idle destination. * @param[in] aircraft Pointer to the aimed aircraft. * @note This function is called when @c aircraft is destroyed. * @sa AIRFIGHT_ActionsAfterAirfight */ void AIRFIGHT_RemoveProjectileAimingAircraft (const aircraft_t* aircraft) { aircraftProjectile_t* projectile; int idx = 0; if (!aircraft) return; for (projectile = ccs.projectiles; idx < ccs.numProjectiles; projectile++, idx++) { if (projectile->aimedAircraft != aircraft) continue; AIRFIGHT_MissTarget(projectile); if (projectile->attackingAircraft && projectile->attackingAircraft->homebase) { assert(!AIR_IsUFO(projectile->attackingAircraft)); AIR_AircraftReturnToBase(projectile->attackingAircraft); } } }