/** * @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 Script function for AIR_AircraftReturnToBase * @note Sends the current aircraft back to homebase (called from aircraft menu). * @note This function updates some cvars for current aircraft. * @sa GAME_CP_MissionAutoGo_f * @sa GAME_CP_Results_f */ static void AIM_AircraftReturnToBase_f (void) { base_t *base = B_GetCurrentSelectedBase(); if (base && base->aircraftCurrent) { AIR_AircraftReturnToBase(base->aircraftCurrent); AIR_AircraftSelect(base->aircraftCurrent); } }
/** * @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); } } }
/** * @brief Change destination of projectile to an idle point of the map, close to its former target. * @param[in] projectile The projectile to update */ static void AIRFIGHT_MissTarget (aircraftProjectile_t *projectile, qboolean returnToBase) { vec3_t newTarget; float distance; float offset; assert(projectile); if (projectile->aimedAircraft) { VectorCopy(projectile->aimedAircraft->pos, newTarget); projectile->aimedAircraft = NULL; } else { VectorCopy(projectile->idleTarget, newTarget); } /* get the distance between the projectile and target */ distance = GetDistanceOnGlobe(projectile->pos[0], newTarget); /* Work out how much the projectile should miss the target by. We dont want it too close * or too far from the original target. * * 1/3 distance between target and projectile * random (range -0.5 to 0.5) * * Then make sure the value is at least greater than 0.1 or less than -0.1 so that * the projectile doesn't land too close to the target. */ offset = (distance / 3) * (frand() - 0.5f); if (abs(offset) < 0.1f) offset = 0.1f; newTarget[0] = newTarget[0] + offset; newTarget[1] = newTarget[1] + offset; VectorCopy(newTarget, projectile->idleTarget); if (returnToBase && projectile->attackingAircraft) { if (projectile->attackingAircraft->homebase) { assert(!AIR_IsUFO(projectile->attackingAircraft)); AIR_AircraftReturnToBase(projectile->attackingAircraft); } } }