mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Move mission range data into the aircraft type.
The doctrine/task limits were capturing a reasonable average for the era, but it did a bad job for cases like the Harrier vs the Hornet, which perform similar missions but have drastically different max ranges. It also forced us into limiting CAS missions (even those flown by long range aircraft like the A-10) to 50nm since helicopters could commonly be fragged to them. This should allow us to design campaigns without needing airfields to be a max of ~50-100nm apart.
This commit is contained in:
parent
04a8040292
commit
c65ac5a7cf
@ -9,6 +9,7 @@ Saves from 3.x are not compatible with 5.0.
|
|||||||
* **[Campaign AI]** Overhauled campaign AI target prioritization. This currently only affects the ordering of DEAD missions.
|
* **[Campaign AI]** Overhauled campaign AI target prioritization. This currently only affects the ordering of DEAD missions.
|
||||||
* **[Campaign AI]** Player front line stances can now be automated. Improved stance selection for AI.
|
* **[Campaign AI]** Player front line stances can now be automated. Improved stance selection for AI.
|
||||||
* **[Campaign AI]** Reworked layout of hold, join, split, and ingress points. Should result in much shorter flight plans in general while still maintaining safe join/split/hold points.
|
* **[Campaign AI]** Reworked layout of hold, join, split, and ingress points. Should result in much shorter flight plans in general while still maintaining safe join/split/hold points.
|
||||||
|
* **[Campaign AI]** Auto-planning mission range limits are now specified per-aircraft. On average this means that longer range missions will now be plannable. The limit only accounts for the direct distance to the target, not the path taken.
|
||||||
* **[New Game Wizard]** Can now customize the player's air wing before campaign start to disable or rename squadrons.
|
* **[New Game Wizard]** Can now customize the player's air wing before campaign start to disable or rename squadrons.
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
|||||||
@ -3,7 +3,8 @@ from typing import Optional, Tuple
|
|||||||
from game.commander.missionproposals import ProposedFlight
|
from game.commander.missionproposals import ProposedFlight
|
||||||
from game.inventory import GlobalAircraftInventory
|
from game.inventory import GlobalAircraftInventory
|
||||||
from game.squadrons import AirWing, Squadron
|
from game.squadrons import AirWing, Squadron
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint, MissionTarget
|
||||||
|
from game.utils import meters
|
||||||
from gen.flights.ai_flight_planner_db import aircraft_for_task
|
from gen.flights.ai_flight_planner_db import aircraft_for_task
|
||||||
from gen.flights.closestairfields import ClosestAirfields
|
from gen.flights.closestairfields import ClosestAirfields
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
@ -25,7 +26,7 @@ class AircraftAllocator:
|
|||||||
self.is_player = is_player
|
self.is_player = is_player
|
||||||
|
|
||||||
def find_squadron_for_flight(
|
def find_squadron_for_flight(
|
||||||
self, flight: ProposedFlight
|
self, target: MissionTarget, flight: ProposedFlight
|
||||||
) -> Optional[Tuple[ControlPoint, Squadron]]:
|
) -> Optional[Tuple[ControlPoint, Squadron]]:
|
||||||
"""Finds aircraft suitable for the given mission.
|
"""Finds aircraft suitable for the given mission.
|
||||||
|
|
||||||
@ -45,17 +46,13 @@ class AircraftAllocator:
|
|||||||
on subsequent calls. If the found aircraft are not used, the caller is
|
on subsequent calls. If the found aircraft are not used, the caller is
|
||||||
responsible for returning them to the inventory.
|
responsible for returning them to the inventory.
|
||||||
"""
|
"""
|
||||||
return self.find_aircraft_for_task(flight, flight.task)
|
return self.find_aircraft_for_task(target, flight, flight.task)
|
||||||
|
|
||||||
def find_aircraft_for_task(
|
def find_aircraft_for_task(
|
||||||
self, flight: ProposedFlight, task: FlightType
|
self, target: MissionTarget, flight: ProposedFlight, task: FlightType
|
||||||
) -> Optional[Tuple[ControlPoint, Squadron]]:
|
) -> Optional[Tuple[ControlPoint, Squadron]]:
|
||||||
types = aircraft_for_task(task)
|
types = aircraft_for_task(task)
|
||||||
airfields_in_range = self.closest_airfields.operational_airfields_within(
|
for airfield in self.closest_airfields.operational_airfields:
|
||||||
flight.max_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
for airfield in airfields_in_range:
|
|
||||||
if not airfield.is_friendly(self.is_player):
|
if not airfield.is_friendly(self.is_player):
|
||||||
continue
|
continue
|
||||||
inventory = self.global_inventory.for_control_point(airfield)
|
inventory = self.global_inventory.for_control_point(airfield)
|
||||||
@ -64,6 +61,9 @@ class AircraftAllocator:
|
|||||||
continue
|
continue
|
||||||
if inventory.available(aircraft) < flight.num_aircraft:
|
if inventory.available(aircraft) < flight.num_aircraft:
|
||||||
continue
|
continue
|
||||||
|
distance_to_target = meters(target.distance_to(airfield))
|
||||||
|
if distance_to_target > aircraft.max_mission_range:
|
||||||
|
continue
|
||||||
# Valid location with enough aircraft available. Find a squadron to fit
|
# Valid location with enough aircraft available. Find a squadron to fit
|
||||||
# the role.
|
# the role.
|
||||||
squadrons = self.air_wing.auto_assignable_for_task_with_type(
|
squadrons = self.air_wing.auto_assignable_for_task_with_type(
|
||||||
|
|||||||
@ -3,7 +3,6 @@ from enum import Enum, auto
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from game.theater import MissionTarget
|
from game.theater import MissionTarget
|
||||||
from game.utils import Distance
|
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
|
|
||||||
@ -27,9 +26,6 @@ class ProposedFlight:
|
|||||||
#: The number of aircraft required.
|
#: The number of aircraft required.
|
||||||
num_aircraft: int
|
num_aircraft: int
|
||||||
|
|
||||||
#: The maximum distance between the objective and the departure airfield.
|
|
||||||
max_distance: Distance
|
|
||||||
|
|
||||||
#: The type of threat this flight defends against if it is an escort. Escort
|
#: The type of threat this flight defends against if it is an escort. Escort
|
||||||
#: flights will be pruned if the rest of the package is not threatened by
|
#: flights will be pruned if the rest of the package is not threatened by
|
||||||
#: the threat they defend against. If this flight is not an escort, this
|
#: the threat they defend against. If this flight is not an escort, this
|
||||||
|
|||||||
@ -44,7 +44,7 @@ class PackageBuilder:
|
|||||||
caller should return any previously planned flights to the inventory
|
caller should return any previously planned flights to the inventory
|
||||||
using release_planned_aircraft.
|
using release_planned_aircraft.
|
||||||
"""
|
"""
|
||||||
assignment = self.allocator.find_squadron_for_flight(plan)
|
assignment = self.allocator.find_squadron_for_flight(self.package.target, plan)
|
||||||
if assignment is None:
|
if assignment is None:
|
||||||
return False
|
return False
|
||||||
airfield, squadron = assignment
|
airfield, squadron = assignment
|
||||||
|
|||||||
@ -83,7 +83,6 @@ class PackageFulfiller:
|
|||||||
missing_types.add(flight.task)
|
missing_types.add(flight.task)
|
||||||
purchase_order = AircraftProcurementRequest(
|
purchase_order = AircraftProcurementRequest(
|
||||||
near=mission.location,
|
near=mission.location,
|
||||||
range=flight.max_distance,
|
|
||||||
task_capability=flight.task,
|
task_capability=flight.task,
|
||||||
number=flight.num_aircraft,
|
number=flight.num_aircraft,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -59,28 +59,23 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
|
|||||||
coalition.ato.add_package(self.package)
|
coalition.ato.add_package(self.package)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
def propose_flight(
|
def propose_flight(
|
||||||
self,
|
self,
|
||||||
task: FlightType,
|
task: FlightType,
|
||||||
num_aircraft: int,
|
num_aircraft: int,
|
||||||
max_distance: Optional[Distance],
|
|
||||||
escort_type: Optional[EscortType] = None,
|
escort_type: Optional[EscortType] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if max_distance is None:
|
self.flights.append(ProposedFlight(task, num_aircraft, escort_type))
|
||||||
max_distance = Distance.inf()
|
|
||||||
self.flights.append(
|
|
||||||
ProposedFlight(task, num_aircraft, max_distance, escort_type)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def asap(self) -> bool:
|
def asap(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def fulfill_mission(self, state: TheaterState) -> bool:
|
def fulfill_mission(self, state: TheaterState) -> bool:
|
||||||
self.propose_flights(state.context.coalition.doctrine)
|
self.propose_flights()
|
||||||
fulfiller = PackageFulfiller(
|
fulfiller = PackageFulfiller(
|
||||||
state.context.coalition,
|
state.context.coalition,
|
||||||
state.context.theater,
|
state.context.theater,
|
||||||
@ -92,20 +87,9 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
|
|||||||
)
|
)
|
||||||
return self.package is not None
|
return self.package is not None
|
||||||
|
|
||||||
def propose_common_escorts(self, doctrine: Doctrine) -> None:
|
def propose_common_escorts(self) -> None:
|
||||||
self.propose_flight(
|
self.propose_flight(FlightType.SEAD_ESCORT, 2, EscortType.Sead)
|
||||||
FlightType.SEAD_ESCORT,
|
self.propose_flight(FlightType.ESCORT, 2, EscortType.AirToAir)
|
||||||
2,
|
|
||||||
doctrine.mission_ranges.offensive,
|
|
||||||
EscortType.Sead,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.propose_flight(
|
|
||||||
FlightType.ESCORT,
|
|
||||||
2,
|
|
||||||
doctrine.mission_ranges.offensive,
|
|
||||||
EscortType.AirToAir,
|
|
||||||
)
|
|
||||||
|
|
||||||
def iter_iads_ranges(
|
def iter_iads_ranges(
|
||||||
self, state: TheaterState, range_type: RangeType
|
self, state: TheaterState, range_type: RangeType
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater import MissionTarget
|
from game.theater import MissionTarget
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -19,8 +18,8 @@ class PlanAewc(PackagePlanningTask[MissionTarget]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.aewc_targets.remove(self.target)
|
state.aewc_targets.remove(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.AEWC, 1, doctrine.mission_ranges.aewc)
|
self.propose_flight(FlightType.AEWC, 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def asap(self) -> bool:
|
def asap(self) -> bool:
|
||||||
|
|||||||
@ -5,7 +5,6 @@ from dataclasses import dataclass
|
|||||||
from game.commander.missionproposals import EscortType
|
from game.commander.missionproposals import EscortType
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater.theatergroundobject import NavalGroundObject
|
from game.theater.theatergroundobject import NavalGroundObject
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -22,11 +21,6 @@ class PlanAntiShip(PackagePlanningTask[NavalGroundObject]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.eliminate_ship(self.target)
|
state.eliminate_ship(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.ANTISHIP, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.ANTISHIP, 2)
|
||||||
self.propose_flight(
|
self.propose_flight(FlightType.ESCORT, 2, EscortType.AirToAir)
|
||||||
FlightType.ESCORT,
|
|
||||||
2,
|
|
||||||
doctrine.mission_ranges.offensive,
|
|
||||||
EscortType.AirToAir,
|
|
||||||
)
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.transfers import CargoShip
|
from game.transfers import CargoShip
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -21,6 +20,6 @@ class PlanAntiShipping(PackagePlanningTask[CargoShip]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.enemy_shipping.remove(self.target)
|
state.enemy_shipping.remove(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.ANTISHIP, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.ANTISHIP, 2)
|
||||||
self.propose_common_escorts(doctrine)
|
self.propose_common_escorts()
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater.theatergroundobject import VehicleGroupGroundObject
|
from game.theater.theatergroundobject import VehicleGroupGroundObject
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -21,6 +20,6 @@ class PlanBai(PackagePlanningTask[VehicleGroupGroundObject]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.eliminate_garrison(self.target)
|
state.eliminate_garrison(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.BAI, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.BAI, 2)
|
||||||
self.propose_common_escorts(doctrine)
|
self.propose_common_escorts()
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -19,5 +18,5 @@ class PlanBarcap(PackagePlanningTask[ControlPoint]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.barcaps_needed[self.target] -= 1
|
state.barcaps_needed[self.target] -= 1
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.BARCAP, 2, doctrine.mission_ranges.cap)
|
self.propose_flight(FlightType.BARCAP, 2)
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater import FrontLine
|
from game.theater import FrontLine
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -19,6 +18,6 @@ class PlanCas(PackagePlanningTask[FrontLine]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.vulnerable_front_lines.remove(self.target)
|
state.vulnerable_front_lines.remove(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.CAS, 2, doctrine.mission_ranges.cas)
|
self.propose_flight(FlightType.CAS, 2)
|
||||||
self.propose_flight(FlightType.TARCAP, 2, doctrine.mission_ranges.cap)
|
self.propose_flight(FlightType.TARCAP, 2)
|
||||||
|
|||||||
@ -21,6 +21,6 @@ class PlanConvoyInterdiction(PackagePlanningTask[Convoy]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.enemy_convoys.remove(self.target)
|
state.enemy_convoys.remove(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.BAI, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.BAI, 2)
|
||||||
self.propose_common_escorts(doctrine)
|
self.propose_common_escorts()
|
||||||
|
|||||||
@ -5,7 +5,6 @@ from dataclasses import dataclass
|
|||||||
from game.commander.missionproposals import EscortType
|
from game.commander.missionproposals import EscortType
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater.theatergroundobject import IadsGroundObject
|
from game.theater.theatergroundobject import IadsGroundObject
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -25,8 +24,8 @@ class PlanDead(PackagePlanningTask[IadsGroundObject]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.eliminate_air_defense(self.target)
|
state.eliminate_air_defense(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.DEAD, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.DEAD, 2)
|
||||||
|
|
||||||
# Only include SEAD against SAMs that still have emitters. No need to
|
# Only include SEAD against SAMs that still have emitters. No need to
|
||||||
# suppress an EWR, and SEAD isn't useful against a SAM that no longer has a
|
# suppress an EWR, and SEAD isn't useful against a SAM that no longer has a
|
||||||
@ -41,18 +40,7 @@ class PlanDead(PackagePlanningTask[IadsGroundObject]):
|
|||||||
# package is *only* threatened by the target though. Could be improved, but
|
# package is *only* threatened by the target though. Could be improved, but
|
||||||
# needs a decent refactor to the escort planning to do so.
|
# needs a decent refactor to the escort planning to do so.
|
||||||
if self.target.has_live_radar_sam:
|
if self.target.has_live_radar_sam:
|
||||||
self.propose_flight(FlightType.SEAD, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.SEAD, 2)
|
||||||
else:
|
else:
|
||||||
self.propose_flight(
|
self.propose_flight(FlightType.SEAD_ESCORT, 2, EscortType.Sead)
|
||||||
FlightType.SEAD_ESCORT,
|
self.propose_flight(FlightType.ESCORT, 2, EscortType.AirToAir)
|
||||||
2,
|
|
||||||
doctrine.mission_ranges.offensive,
|
|
||||||
EscortType.Sead,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.propose_flight(
|
|
||||||
FlightType.ESCORT,
|
|
||||||
2,
|
|
||||||
doctrine.mission_ranges.offensive,
|
|
||||||
EscortType.AirToAir,
|
|
||||||
)
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -23,10 +22,8 @@ class PlanOcaStrike(PackagePlanningTask[ControlPoint]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.oca_targets.remove(self.target)
|
state.oca_targets.remove(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.OCA_RUNWAY, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.OCA_RUNWAY, 2)
|
||||||
if self.aircraft_cold_start:
|
if self.aircraft_cold_start:
|
||||||
self.propose_flight(
|
self.propose_flight(FlightType.OCA_AIRCRAFT, 2)
|
||||||
FlightType.OCA_AIRCRAFT, 2, doctrine.mission_ranges.offensive
|
self.propose_common_escorts()
|
||||||
)
|
|
||||||
self.propose_common_escorts(doctrine)
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater import MissionTarget
|
from game.theater import MissionTarget
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -19,5 +18,5 @@ class PlanRefueling(PackagePlanningTask[MissionTarget]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.refueling_targets.remove(self.target)
|
state.refueling_targets.remove(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.REFUELING, 1, doctrine.mission_ranges.refueling)
|
self.propose_flight(FlightType.REFUELING, 1)
|
||||||
|
|||||||
@ -5,7 +5,6 @@ from typing import Any
|
|||||||
|
|
||||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.data.doctrine import Doctrine
|
|
||||||
from game.theater.theatergroundobject import TheaterGroundObject
|
from game.theater.theatergroundobject import TheaterGroundObject
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
|
|
||||||
@ -22,6 +21,6 @@ class PlanStrike(PackagePlanningTask[TheaterGroundObject[Any]]):
|
|||||||
def apply_effects(self, state: TheaterState) -> None:
|
def apply_effects(self, state: TheaterState) -> None:
|
||||||
state.strike_targets.remove(self.target)
|
state.strike_targets.remove(self.target)
|
||||||
|
|
||||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
def propose_flights(self) -> None:
|
||||||
self.propose_flight(FlightType.STRIKE, 2, doctrine.mission_ranges.offensive)
|
self.propose_flight(FlightType.STRIKE, 2)
|
||||||
self.propose_common_escorts(doctrine)
|
self.propose_common_escorts()
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@ -18,15 +18,6 @@ class GroundUnitProcurementRatios:
|
|||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class MissionPlannerMaxRanges:
|
|
||||||
cap: Distance = field(default=nautical_miles(100))
|
|
||||||
cas: Distance = field(default=nautical_miles(50))
|
|
||||||
offensive: Distance = field(default=nautical_miles(150))
|
|
||||||
aewc: Distance = field(default=Distance.inf())
|
|
||||||
refueling: Distance = field(default=nautical_miles(200))
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Doctrine:
|
class Doctrine:
|
||||||
cas: bool
|
cas: bool
|
||||||
@ -88,8 +79,6 @@ class Doctrine:
|
|||||||
|
|
||||||
ground_unit_procurement_ratios: GroundUnitProcurementRatios
|
ground_unit_procurement_ratios: GroundUnitProcurementRatios
|
||||||
|
|
||||||
mission_ranges: MissionPlannerMaxRanges = field(default=MissionPlannerMaxRanges())
|
|
||||||
|
|
||||||
@has_save_compat_for(5)
|
@has_save_compat_for(5)
|
||||||
def __setstate__(self, state: dict[str, Any]) -> None:
|
def __setstate__(self, state: dict[str, Any]) -> None:
|
||||||
if "max_ingress_distance" not in state:
|
if "max_ingress_distance" not in state:
|
||||||
@ -111,6 +100,12 @@ class Doctrine:
|
|||||||
self.__dict__.update(state)
|
self.__dict__.update(state)
|
||||||
|
|
||||||
|
|
||||||
|
class MissionPlannerMaxRanges:
|
||||||
|
@has_save_compat_for(5)
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
MODERN_DOCTRINE = Doctrine(
|
MODERN_DOCTRINE = Doctrine(
|
||||||
cap=True,
|
cap=True,
|
||||||
cas=True,
|
cas=True,
|
||||||
|
|||||||
@ -29,7 +29,7 @@ from game.radio.channels import (
|
|||||||
ViggenRadioChannelAllocator,
|
ViggenRadioChannelAllocator,
|
||||||
NoOpChannelAllocator,
|
NoOpChannelAllocator,
|
||||||
)
|
)
|
||||||
from game.utils import Distance, Speed, feet, kph, knots
|
from game.utils import Distance, Speed, feet, kph, knots, nautical_miles
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from gen.aircraft import FlightData
|
from gen.aircraft import FlightData
|
||||||
@ -112,13 +112,18 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
lha_capable: bool
|
lha_capable: bool
|
||||||
always_keeps_gun: bool
|
always_keeps_gun: bool
|
||||||
|
|
||||||
# If true, the aircraft does not use the guns as the last resort weapons, but as a main weapon.
|
# If true, the aircraft does not use the guns as the last resort weapons, but as a
|
||||||
# It'll RTB when it doesn't have gun ammo left.
|
# main weapon. It'll RTB when it doesn't have gun ammo left.
|
||||||
gunfighter: bool
|
gunfighter: bool
|
||||||
|
|
||||||
max_group_size: int
|
max_group_size: int
|
||||||
patrol_altitude: Optional[Distance]
|
patrol_altitude: Optional[Distance]
|
||||||
patrol_speed: Optional[Speed]
|
patrol_speed: Optional[Speed]
|
||||||
|
|
||||||
|
#: The maximum range between the origin airfield and the target for which the auto-
|
||||||
|
#: planner will consider this aircraft usable for a mission.
|
||||||
|
max_mission_range: Distance
|
||||||
|
|
||||||
intra_flight_radio: Optional[Radio]
|
intra_flight_radio: Optional[Radio]
|
||||||
channel_allocator: Optional[RadioChannelAllocator]
|
channel_allocator: Optional[RadioChannelAllocator]
|
||||||
channel_namer: Type[ChannelNamer]
|
channel_namer: Type[ChannelNamer]
|
||||||
@ -230,6 +235,13 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
radio_config = RadioConfig.from_data(data.get("radios", {}))
|
radio_config = RadioConfig.from_data(data.get("radios", {}))
|
||||||
patrol_config = PatrolConfig.from_data(data.get("patrol", {}))
|
patrol_config = PatrolConfig.from_data(data.get("patrol", {}))
|
||||||
|
|
||||||
|
try:
|
||||||
|
mission_range = nautical_miles(int(data["max_range"]))
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
mission_range = (
|
||||||
|
nautical_miles(50) if aircraft.helicopter else nautical_miles(150)
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
introduction = data["introduced"]
|
introduction = data["introduced"]
|
||||||
if introduction is None:
|
if introduction is None:
|
||||||
@ -257,6 +269,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
max_group_size=data.get("max_group_size", aircraft.group_size_max),
|
max_group_size=data.get("max_group_size", aircraft.group_size_max),
|
||||||
patrol_altitude=patrol_config.altitude,
|
patrol_altitude=patrol_config.altitude,
|
||||||
patrol_speed=patrol_config.speed,
|
patrol_speed=patrol_config.speed,
|
||||||
|
max_mission_range=mission_range,
|
||||||
intra_flight_radio=radio_config.intra_flight,
|
intra_flight_radio=radio_config.intra_flight,
|
||||||
channel_allocator=radio_config.channel_allocator,
|
channel_allocator=radio_config.channel_allocator,
|
||||||
channel_namer=radio_config.channel_namer,
|
channel_namer=radio_config.channel_namer,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from game.dcs.aircrafttype import AircraftType
|
|||||||
from game.dcs.groundunittype import GroundUnitType
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.factions.faction import Faction
|
from game.factions.faction import Faction
|
||||||
from game.theater import ControlPoint, MissionTarget
|
from game.theater import ControlPoint, MissionTarget
|
||||||
from game.utils import Distance
|
from game.utils import meters
|
||||||
from gen.flights.ai_flight_planner_db import aircraft_for_task
|
from gen.flights.ai_flight_planner_db import aircraft_for_task
|
||||||
from gen.flights.closestairfields import ObjectiveDistanceCache
|
from gen.flights.closestairfields import ObjectiveDistanceCache
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
@ -25,15 +25,13 @@ FRONTLINE_RESERVES_FACTOR = 1.3
|
|||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AircraftProcurementRequest:
|
class AircraftProcurementRequest:
|
||||||
near: MissionTarget
|
near: MissionTarget
|
||||||
range: Distance
|
|
||||||
task_capability: FlightType
|
task_capability: FlightType
|
||||||
number: int
|
number: int
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
task = self.task_capability.value
|
task = self.task_capability.value
|
||||||
distance = self.range.nautical_miles
|
|
||||||
target = self.near.name
|
target = self.near.name
|
||||||
return f"{self.number} ship {task} within {distance} nm of {target}"
|
return f"{self.number} ship {task} near {target}"
|
||||||
|
|
||||||
|
|
||||||
class ProcurementAi:
|
class ProcurementAi:
|
||||||
@ -211,24 +209,24 @@ class ProcurementAi:
|
|||||||
return GroundUnitClass.Tank
|
return GroundUnitClass.Tank
|
||||||
return worst_balanced
|
return worst_balanced
|
||||||
|
|
||||||
def _affordable_aircraft_for_task(
|
def affordable_aircraft_for(
|
||||||
self,
|
self, request: AircraftProcurementRequest, airbase: ControlPoint, budget: float
|
||||||
task: FlightType,
|
|
||||||
airbase: ControlPoint,
|
|
||||||
number: int,
|
|
||||||
max_price: float,
|
|
||||||
) -> Optional[AircraftType]:
|
) -> Optional[AircraftType]:
|
||||||
best_choice: Optional[AircraftType] = None
|
best_choice: Optional[AircraftType] = None
|
||||||
for unit in aircraft_for_task(task):
|
for unit in aircraft_for_task(request.task_capability):
|
||||||
if unit not in self.faction.aircrafts:
|
if unit not in self.faction.aircrafts:
|
||||||
continue
|
continue
|
||||||
if unit.price * number > max_price:
|
if unit.price * request.number > budget:
|
||||||
continue
|
continue
|
||||||
if not airbase.can_operate(unit):
|
if not airbase.can_operate(unit):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
distance_to_target = meters(request.near.distance_to(airbase))
|
||||||
|
if distance_to_target > unit.max_mission_range:
|
||||||
|
continue
|
||||||
|
|
||||||
for squadron in self.air_wing.squadrons_for(unit):
|
for squadron in self.air_wing.squadrons_for(unit):
|
||||||
if task in squadron.auto_assignable_mission_types:
|
if request.task_capability in squadron.auto_assignable_mission_types:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
@ -241,13 +239,6 @@ class ProcurementAi:
|
|||||||
break
|
break
|
||||||
return best_choice
|
return best_choice
|
||||||
|
|
||||||
def affordable_aircraft_for(
|
|
||||||
self, request: AircraftProcurementRequest, airbase: ControlPoint, budget: float
|
|
||||||
) -> Optional[AircraftType]:
|
|
||||||
return self._affordable_aircraft_for_task(
|
|
||||||
request.task_capability, airbase, request.number, budget
|
|
||||||
)
|
|
||||||
|
|
||||||
def fulfill_aircraft_request(
|
def fulfill_aircraft_request(
|
||||||
self, request: AircraftProcurementRequest, budget: float
|
self, request: AircraftProcurementRequest, budget: float
|
||||||
) -> Tuple[float, bool]:
|
) -> Tuple[float, bool]:
|
||||||
@ -293,7 +284,7 @@ class ProcurementAi:
|
|||||||
) -> Iterator[ControlPoint]:
|
) -> Iterator[ControlPoint]:
|
||||||
distance_cache = ObjectiveDistanceCache.get_closest_airfields(request.near)
|
distance_cache = ObjectiveDistanceCache.get_closest_airfields(request.near)
|
||||||
threatened = []
|
threatened = []
|
||||||
for cp in distance_cache.operational_airfields_within(request.range):
|
for cp in distance_cache.operational_airfields:
|
||||||
if not cp.is_friendly(self.is_player):
|
if not cp.is_friendly(self.is_player):
|
||||||
continue
|
continue
|
||||||
if cp.unclaimed_parking(self.game) < request.number:
|
if cp.unclaimed_parking(self.game) < request.number:
|
||||||
|
|||||||
@ -688,7 +688,5 @@ class PendingTransfers:
|
|||||||
gap += 1
|
gap += 1
|
||||||
|
|
||||||
self.game.procurement_requests_for(self.player).append(
|
self.game.procurement_requests_for(self.player).append(
|
||||||
AircraftProcurementRequest(
|
AircraftProcurementRequest(control_point, FlightType.TRANSPORT, gap)
|
||||||
control_point, nautical_miles(200), FlightType.TRANSPORT, gap
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
description: The A-50 is an AWACS plane.
|
description: The A-50 is an AWACS plane.
|
||||||
max_group_size: 1
|
max_group_size: 1
|
||||||
|
max_range: 2000
|
||||||
price: 50
|
price: 50
|
||||||
patrol:
|
patrol:
|
||||||
altitude: 33000
|
altitude: 33000
|
||||||
|
|||||||
@ -27,6 +27,7 @@ manufacturer: McDonnell Douglas
|
|||||||
origin: USA/UK
|
origin: USA/UK
|
||||||
price: 15
|
price: 15
|
||||||
role: V/STOL Attack
|
role: V/STOL Attack
|
||||||
|
max_range: 100
|
||||||
variants:
|
variants:
|
||||||
AV-8B Harrier II Night Attack: {}
|
AV-8B Harrier II Night Attack: {}
|
||||||
radios:
|
radios:
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The An-26B is a military transport aircraft.
|
description: The An-26B is a military transport aircraft.
|
||||||
price: 15
|
price: 15
|
||||||
|
max_range: 800
|
||||||
variants:
|
variants:
|
||||||
An-26B: null
|
An-26B: null
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The Rockwell B-1 Lancer is a supersonic variable-sweep wing, heavy bomber
|
description:
|
||||||
|
The Rockwell B-1 Lancer is a supersonic variable-sweep wing, heavy bomber
|
||||||
used by the United States Air Force. It is commonly called the 'Bone' (from 'B-One').It
|
used by the United States Air Force. It is commonly called the 'Bone' (from 'B-One').It
|
||||||
is one of three strategic bombers in the U.S. Air Force fleet as of 2021, the other
|
is one of three strategic bombers in the U.S. Air Force fleet as of 2021, the other
|
||||||
two being the B-2 Spirit and the B-52 Stratofortress. It first served in combat
|
two being the B-2 Spirit and the B-52 Stratofortress. It first served in combat
|
||||||
@ -12,5 +13,6 @@ manufacturer: Rockwell
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 45
|
price: 45
|
||||||
role: Supersonic Strategic Bomber
|
role: Supersonic Strategic Bomber
|
||||||
|
max_range: 2000
|
||||||
variants:
|
variants:
|
||||||
B-1B Lancer: {}
|
B-1B Lancer: {}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The Boeing B-52 Stratofortress is capable of carrying up to 70,000 pounds
|
description:
|
||||||
|
The Boeing B-52 Stratofortress is capable of carrying up to 70,000 pounds
|
||||||
(32,000 kg) of weapons, and has a typical combat range of more than 8,800 miles
|
(32,000 kg) of weapons, and has a typical combat range of more than 8,800 miles
|
||||||
(14,080 km) without aerial refueling. The B-52 completed sixty years of continuous
|
(14,080 km) without aerial refueling. The B-52 completed sixty years of continuous
|
||||||
service with its original operator in 2015. After being upgraded between 2013 and
|
service with its original operator in 2015. After being upgraded between 2013 and
|
||||||
@ -8,5 +9,6 @@ manufacturer: Boeing
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 35
|
price: 35
|
||||||
role: Strategic Bomber
|
role: Strategic Bomber
|
||||||
|
max_range: 2000
|
||||||
variants:
|
variants:
|
||||||
B-52H Stratofortress: {}
|
B-52H Stratofortress: {}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The C-130 is a military transport aircraft.
|
description: The C-130 is a military transport aircraft.
|
||||||
price: 15
|
price: 15
|
||||||
|
max_range: 1000
|
||||||
variants:
|
variants:
|
||||||
C-130: null
|
C-130: null
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The C-17 is a military transport aircraft.
|
description: The C-17 is a military transport aircraft.
|
||||||
price: 18
|
price: 18
|
||||||
|
max_range: 2000
|
||||||
variants:
|
variants:
|
||||||
C-17A: null
|
C-17A: null
|
||||||
|
|||||||
@ -8,6 +8,7 @@ manufacturer: Northrop Grumman
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 50
|
price: 50
|
||||||
role: AEW&C
|
role: AEW&C
|
||||||
|
max_range: 2000
|
||||||
patrol:
|
patrol:
|
||||||
altitude: 30000
|
altitude: 30000
|
||||||
variants:
|
variants:
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
description: The E-3A is a AWACS aicraft.
|
description: The E-3A is a AWACS aicraft.
|
||||||
price: 50
|
price: 50
|
||||||
max_group_size: 1
|
max_group_size: 1
|
||||||
|
max_range: 2000
|
||||||
patrol:
|
patrol:
|
||||||
altitude: 35000
|
altitude: 35000
|
||||||
variants:
|
variants:
|
||||||
|
|||||||
@ -21,6 +21,7 @@ manufacturer: Grumman
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 22
|
price: 22
|
||||||
role: Carrier-based Air-Superiority Fighter/Fighter Bomber
|
role: Carrier-based Air-Superiority Fighter/Fighter Bomber
|
||||||
|
max_range: 250
|
||||||
variants:
|
variants:
|
||||||
F-14A Tomcat (Block 135-GR Late): {}
|
F-14A Tomcat (Block 135-GR Late): {}
|
||||||
radios:
|
radios:
|
||||||
|
|||||||
@ -21,6 +21,7 @@ manufacturer: Grumman
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 26
|
price: 26
|
||||||
role: Carrier-based Air-Superiority Fighter/Fighter Bomber
|
role: Carrier-based Air-Superiority Fighter/Fighter Bomber
|
||||||
|
max_range: 250
|
||||||
variants:
|
variants:
|
||||||
F-14B Tomcat: {}
|
F-14B Tomcat: {}
|
||||||
radios:
|
radios:
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The early verison of the F-16. It flew in Desert Storm.
|
description: The early verison of the F-16. It flew in Desert Storm.
|
||||||
price: 15
|
price: 15
|
||||||
|
max_range: 200
|
||||||
variants:
|
variants:
|
||||||
F-16A: null
|
F-16A: null
|
||||||
|
|||||||
@ -27,6 +27,7 @@ manufacturer: General Dynamics
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 22
|
price: 22
|
||||||
role: Multirole Fighter
|
role: Multirole Fighter
|
||||||
|
max_range: 200
|
||||||
variants:
|
variants:
|
||||||
F-16CM Fighting Falcon (Block 50): {}
|
F-16CM Fighting Falcon (Block 50): {}
|
||||||
F-2A: {}
|
F-2A: {}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The Lockheed Martin C-130J Super Hercules is a four-engine turboprop
|
description:
|
||||||
|
The Lockheed Martin C-130J Super Hercules is a four-engine turboprop
|
||||||
military transport aircraft. The C-130J is a comprehensive update of the Lockheed
|
military transport aircraft. The C-130J is a comprehensive update of the Lockheed
|
||||||
C-130 Hercules, with new engines, flight deck, and other systems. As of February
|
C-130 Hercules, with new engines, flight deck, and other systems. As of February
|
||||||
2018, 400 C-130J aircraft have been delivered to 17 nations.
|
2018, 400 C-130J aircraft have been delivered to 17 nations.
|
||||||
@ -7,5 +8,6 @@ manufacturer: Lockheed
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 18
|
price: 18
|
||||||
role: Transport
|
role: Transport
|
||||||
|
max_range: 1000
|
||||||
variants:
|
variants:
|
||||||
C-130J-30 Super Hercules: {}
|
C-130J-30 Super Hercules: {}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
price: 20
|
price: 20
|
||||||
|
max_range: 1000
|
||||||
variants:
|
variants:
|
||||||
IL-76MD: null
|
IL-76MD: null
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
price: 20
|
price: 20
|
||||||
max_group_size: 1
|
max_group_size: 1
|
||||||
|
max_range: 1000
|
||||||
patrol:
|
patrol:
|
||||||
# ~280 knots IAS.
|
# ~280 knots IAS.
|
||||||
speed: 400
|
speed: 400
|
||||||
|
|||||||
@ -8,6 +8,7 @@ manufacturer: Beoing
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 25
|
price: 25
|
||||||
role: Tanker
|
role: Tanker
|
||||||
|
max_range: 1000
|
||||||
patrol:
|
patrol:
|
||||||
# ~300 knots IAS.
|
# ~300 knots IAS.
|
||||||
speed: 445
|
speed: 445
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
description: The Lockheed Martin (previously Lockheed) KC-130 is a family of the extended-range
|
description:
|
||||||
|
The Lockheed Martin (previously Lockheed) KC-130 is a family of the extended-range
|
||||||
tanker version of the C-130 Hercules transport aircraft modified for aerial refueling.
|
tanker version of the C-130 Hercules transport aircraft modified for aerial refueling.
|
||||||
introduced: 1962
|
introduced: 1962
|
||||||
manufacturer: Lockheed Martin
|
manufacturer: Lockheed Martin
|
||||||
origin: USA
|
origin: USA
|
||||||
price: 25
|
price: 25
|
||||||
role: Tanker
|
role: Tanker
|
||||||
|
max_range: 1000
|
||||||
patrol:
|
patrol:
|
||||||
# ~210 knots IAS, roughly the max for the KC-130 at altitude.
|
# ~210 knots IAS, roughly the max for the KC-130 at altitude.
|
||||||
speed: 370
|
speed: 370
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
description: The Boeing KC-135 Stratotanker is a military aerial refueling aircraft
|
description:
|
||||||
|
The Boeing KC-135 Stratotanker is a military aerial refueling aircraft
|
||||||
that was developed from the Boeing 367-80 prototype, alongside the Boeing 707 airliner. This
|
that was developed from the Boeing 367-80 prototype, alongside the Boeing 707 airliner. This
|
||||||
model has the Multi-point Refueling System modification, allowing for probe and
|
model has the Multi-point Refueling System modification, allowing for probe and
|
||||||
drogue refuelling.
|
drogue refuelling.
|
||||||
@ -7,6 +8,7 @@ manufacturer: Boeing
|
|||||||
origin: USA
|
origin: USA
|
||||||
price: 25
|
price: 25
|
||||||
role: Tanker
|
role: Tanker
|
||||||
|
max_range: 1000
|
||||||
patrol:
|
patrol:
|
||||||
# 300 knots IAS.
|
# 300 knots IAS.
|
||||||
speed: 440
|
speed: 440
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
price: 50
|
price: 50
|
||||||
|
max_range: 2000
|
||||||
patrol:
|
patrol:
|
||||||
altitude: 40000
|
altitude: 40000
|
||||||
variants:
|
variants:
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
carrier_capable: true
|
carrier_capable: true
|
||||||
description: The Lockheed S-3 Viking is a 4-crew, twin-engine turbofan-powered jet
|
description:
|
||||||
|
The Lockheed S-3 Viking is a 4-crew, twin-engine turbofan-powered jet
|
||||||
aircraft that was used by the U.S. Navy (USN) primarily for anti-submarine warfare.
|
aircraft that was used by the U.S. Navy (USN) primarily for anti-submarine warfare.
|
||||||
In the late 1990s, the S-3B's mission focus shifted to surface warfare and aerial
|
In the late 1990s, the S-3B's mission focus shifted to surface warfare and aerial
|
||||||
refueling. The Viking also provided electronic warfare and surface surveillance
|
refueling. The Viking also provided electronic warfare and surface surveillance
|
||||||
@ -16,6 +17,7 @@ origin: USA
|
|||||||
price: 20
|
price: 20
|
||||||
max_group_size: 1
|
max_group_size: 1
|
||||||
role: Carrier-based Tanker
|
role: Carrier-based Tanker
|
||||||
|
max_range: 1000
|
||||||
patrol:
|
patrol:
|
||||||
# ~265 knots IAS.
|
# ~265 knots IAS.
|
||||||
speed: 320
|
speed: 320
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
price: 25
|
price: 25
|
||||||
|
max_range: 600
|
||||||
variants:
|
variants:
|
||||||
Yak-40: null
|
Yak-40: null
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user