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