mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Improve unit system support in kneeboards.
* Factor out unit systems. * Add support for more unit systems (nautical and imperial). * Fuel units support. * Data for many more aircraft.
This commit is contained in:
parent
cd97565cce
commit
4b99ae957e
@ -33,7 +33,11 @@ from game.radio.channels import (
|
|||||||
from game.utils import (
|
from game.utils import (
|
||||||
Distance,
|
Distance,
|
||||||
SPEED_OF_SOUND_AT_SEA_LEVEL,
|
SPEED_OF_SOUND_AT_SEA_LEVEL,
|
||||||
|
ImperialUnits,
|
||||||
|
MetricUnits,
|
||||||
|
NauticalUnits,
|
||||||
Speed,
|
Speed,
|
||||||
|
UnitSystem,
|
||||||
feet,
|
feet,
|
||||||
knots,
|
knots,
|
||||||
kph,
|
kph,
|
||||||
@ -154,8 +158,8 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
# main weapon. 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
|
||||||
|
|
||||||
# If true, kneeboards will be generated in metric units
|
# UnitSystem to use for the kneeboard, defaults to Nautical (kt/nm/ft)
|
||||||
metric_kneeboard: bool
|
kneeboard_units: UnitSystem
|
||||||
|
|
||||||
# If true, kneeboards will display zulu times
|
# If true, kneeboards will display zulu times
|
||||||
utc_kneeboard: bool
|
utc_kneeboard: bool
|
||||||
@ -376,6 +380,13 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
introduction = "No data."
|
introduction = "No data."
|
||||||
|
|
||||||
|
units_data = data.get("kneeboard_units", "nautical").lower()
|
||||||
|
units: UnitSystem = NauticalUnits()
|
||||||
|
if units_data == "imperial":
|
||||||
|
units = ImperialUnits()
|
||||||
|
if units_data == "metric":
|
||||||
|
units = MetricUnits()
|
||||||
|
|
||||||
for variant in data.get("variants", [aircraft.id]):
|
for variant in data.get("variants", [aircraft.id]):
|
||||||
yield AircraftType(
|
yield AircraftType(
|
||||||
dcs_unit_type=aircraft,
|
dcs_unit_type=aircraft,
|
||||||
@ -401,6 +412,6 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
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,
|
||||||
metric_kneeboard=data.get("metric_kneeboard", False),
|
kneeboard_units=units,
|
||||||
utc_kneeboard=data.get("utc_kneeboard", False),
|
utc_kneeboard=data.get("utc_kneeboard", False),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -44,7 +44,7 @@ from game.dcs.aircrafttype import AircraftType
|
|||||||
from game.radio.radios import RadioFrequency
|
from game.radio.radios import RadioFrequency
|
||||||
from game.theater import ConflictTheater, LatLon, TheaterGroundObject
|
from game.theater import ConflictTheater, LatLon, TheaterGroundObject
|
||||||
from game.theater.bullseye import Bullseye
|
from game.theater.bullseye import Bullseye
|
||||||
from game.utils import Distance, meters
|
from game.utils import Distance, UnitSystem, meters, mps, pounds
|
||||||
from game.weather import Weather
|
from game.weather import Weather
|
||||||
from gen.runways import RunwayData
|
from gen.runways import RunwayData
|
||||||
from .aircraft.flightdata import FlightData
|
from .aircraft.flightdata import FlightData
|
||||||
@ -202,12 +202,12 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
WAYPOINT_DESC_MAX_LEN = 25
|
WAYPOINT_DESC_MAX_LEN = 25
|
||||||
|
|
||||||
def __init__(self, start_time: datetime.datetime, is_metric: bool) -> None:
|
def __init__(self, start_time: datetime.datetime, units: UnitSystem) -> None:
|
||||||
self.start_time = start_time
|
self.start_time = start_time
|
||||||
self.rows: List[List[str]] = []
|
self.rows: List[List[str]] = []
|
||||||
self.target_points: List[NumberedWaypoint] = []
|
self.target_points: List[NumberedWaypoint] = []
|
||||||
self.last_waypoint: Optional[FlightWaypoint] = None
|
self.last_waypoint: Optional[FlightWaypoint] = None
|
||||||
self.is_metric = is_metric
|
self.units = units
|
||||||
|
|
||||||
def add_waypoint(self, waypoint_num: int, waypoint: FlightWaypoint) -> None:
|
def add_waypoint(self, waypoint_num: int, waypoint: FlightWaypoint) -> None:
|
||||||
if waypoint.waypoint_type == FlightWaypointType.TARGET_POINT:
|
if waypoint.waypoint_type == FlightWaypointType.TARGET_POINT:
|
||||||
@ -269,32 +269,24 @@ class FlightPlanBuilder:
|
|||||||
return f"{local_time.strftime('%H:%M:%S')}{'Z' if local_time.tzinfo is not None else ''}"
|
return f"{local_time.strftime('%H:%M:%S')}{'Z' if local_time.tzinfo is not None else ''}"
|
||||||
|
|
||||||
def _format_alt(self, alt: Distance) -> str:
|
def _format_alt(self, alt: Distance) -> str:
|
||||||
if self.is_metric:
|
return f"{self.units.distance_short(alt):.0f}"
|
||||||
return f"{alt.meters:.0f} m"
|
|
||||||
else:
|
|
||||||
return f"{alt.feet:.0f} ft"
|
|
||||||
|
|
||||||
def _waypoint_distance(self, waypoint: FlightWaypoint) -> str:
|
def _waypoint_distance(self, waypoint: FlightWaypoint) -> str:
|
||||||
if self.last_waypoint is None:
|
if self.last_waypoint is None:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
if self.is_metric:
|
distance = meters(
|
||||||
distance = meters(
|
self.last_waypoint.position.distance_to_point(waypoint.position)
|
||||||
self.last_waypoint.position.distance_to_point(waypoint.position)
|
)
|
||||||
)
|
|
||||||
return f"{(distance.meters / 1000):.1f} km"
|
return f"{self.units.distance_long(distance):.1f}"
|
||||||
else:
|
|
||||||
distance = meters(
|
|
||||||
self.last_waypoint.position.distance_to_point(waypoint.position)
|
|
||||||
)
|
|
||||||
return f"{distance.nautical_miles:.1f} nm"
|
|
||||||
|
|
||||||
def _waypoint_bearing(self, waypoint: FlightWaypoint) -> str:
|
def _waypoint_bearing(self, waypoint: FlightWaypoint) -> str:
|
||||||
if self.last_waypoint is None:
|
if self.last_waypoint is None:
|
||||||
return "-"
|
return "-"
|
||||||
bearing = self.last_waypoint.position.heading_between_point(waypoint.position)
|
bearing = self.last_waypoint.position.heading_between_point(waypoint.position)
|
||||||
|
|
||||||
return f"{(bearing):.0f} T"
|
return f"{(bearing):.0f}"
|
||||||
|
|
||||||
def _ground_speed(self, waypoint: FlightWaypoint) -> str:
|
def _ground_speed(self, waypoint: FlightWaypoint) -> str:
|
||||||
if self.last_waypoint is None:
|
if self.last_waypoint is None:
|
||||||
@ -310,20 +302,19 @@ class FlightPlanBuilder:
|
|||||||
else:
|
else:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
distance = meters(
|
speed = mps(
|
||||||
self.last_waypoint.position.distance_to_point(waypoint.position)
|
self.last_waypoint.position.distance_to_point(waypoint.position)
|
||||||
|
/ (waypoint.tot - last_time).total_seconds()
|
||||||
)
|
)
|
||||||
duration = (waypoint.tot - last_time).total_seconds() / 3600
|
|
||||||
if self.is_metric:
|
|
||||||
return f"{int((distance.meters / 1000) / duration)} km/h"
|
|
||||||
else:
|
|
||||||
return f"{int(distance.nautical_miles / duration)} kt"
|
|
||||||
|
|
||||||
@staticmethod
|
return f"{self.units.speed(speed):.0f}"
|
||||||
def _format_min_fuel(min_fuel: Optional[float]) -> str:
|
|
||||||
|
def _format_min_fuel(self, min_fuel: Optional[float]) -> str:
|
||||||
if min_fuel is None:
|
if min_fuel is None:
|
||||||
return ""
|
return ""
|
||||||
return str(math.ceil(min_fuel / 100) * 100)
|
|
||||||
|
mass = pounds(min_fuel)
|
||||||
|
return f"{math.ceil(self.units.mass(mass) / 100) * 100:.0f}"
|
||||||
|
|
||||||
def build(self) -> List[List[str]]:
|
def build(self) -> List[List[str]]:
|
||||||
return self.rows
|
return self.rows
|
||||||
@ -373,13 +364,29 @@ class BriefingPage(KneeboardPage):
|
|||||||
)
|
)
|
||||||
|
|
||||||
writer.heading("Flight Plan")
|
writer.heading("Flight Plan")
|
||||||
flight_plan_builder = FlightPlanBuilder(
|
|
||||||
self.start_time, self.flight.aircraft_type.metric_kneeboard
|
units = self.flight.aircraft_type.kneeboard_units
|
||||||
)
|
|
||||||
|
flight_plan_builder = FlightPlanBuilder(self.start_time, units)
|
||||||
for num, waypoint in enumerate(self.flight.waypoints):
|
for num, waypoint in enumerate(self.flight.waypoints):
|
||||||
flight_plan_builder.add_waypoint(num, waypoint)
|
flight_plan_builder.add_waypoint(num, waypoint)
|
||||||
|
|
||||||
|
uom_row = [
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
units.distance_short_uom,
|
||||||
|
units.distance_long_uom,
|
||||||
|
"T",
|
||||||
|
units.speed_uom,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
units.mass_uom,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
writer.table(
|
writer.table(
|
||||||
flight_plan_builder.build(),
|
flight_plan_builder.build() + uom_row,
|
||||||
headers=[
|
headers=[
|
||||||
"#",
|
"#",
|
||||||
"Action",
|
"Action",
|
||||||
@ -404,15 +411,18 @@ class BriefingPage(KneeboardPage):
|
|||||||
)
|
)
|
||||||
writer.text(f"QNH: {qnh_in_hg} inHg / {qnh_mm_hg} mmHg / {qnh_hpa} hPa")
|
writer.text(f"QNH: {qnh_in_hg} inHg / {qnh_mm_hg} mmHg / {qnh_hpa} hPa")
|
||||||
|
|
||||||
writer.table(
|
fl = self.flight
|
||||||
[
|
|
||||||
|
if fl.bingo_fuel and fl.joker_fuel:
|
||||||
|
writer.table(
|
||||||
[
|
[
|
||||||
"{}lbs".format(self.flight.bingo_fuel),
|
[
|
||||||
"{}lbs".format(self.flight.joker_fuel),
|
f"{units.mass(pounds(fl.bingo_fuel)):.0f} {units.mass_uom}",
|
||||||
]
|
f"{units.mass(pounds(fl.joker_fuel)):.0f} {units.mass_uom}",
|
||||||
],
|
]
|
||||||
["Bingo", "Joker"],
|
],
|
||||||
)
|
["Bingo", "Joker"],
|
||||||
|
)
|
||||||
|
|
||||||
if any(self.flight.laser_codes):
|
if any(self.flight.laser_codes):
|
||||||
codes: list[list[str]] = []
|
codes: list[list[str]] = []
|
||||||
|
|||||||
167
game/utils.py
167
game/utils.py
@ -1,4 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import math
|
import math
|
||||||
@ -14,16 +15,149 @@ METERS_TO_FEET = 3.28084
|
|||||||
FEET_TO_METERS = 1 / METERS_TO_FEET
|
FEET_TO_METERS = 1 / METERS_TO_FEET
|
||||||
NM_TO_METERS = 1852
|
NM_TO_METERS = 1852
|
||||||
METERS_TO_NM = 1 / NM_TO_METERS
|
METERS_TO_NM = 1 / NM_TO_METERS
|
||||||
|
MILES_TO_METERS = 1609.34
|
||||||
|
METERS_TO_MILES = 1 / MILES_TO_METERS
|
||||||
|
|
||||||
KNOTS_TO_KPH = 1.852
|
KNOTS_TO_KPH = 1.852
|
||||||
KPH_TO_KNOTS = 1 / KNOTS_TO_KPH
|
KPH_TO_KNOTS = 1 / KNOTS_TO_KPH
|
||||||
MS_TO_KPH = 3.6
|
MS_TO_KPH = 3.6
|
||||||
KPH_TO_MS = 1 / MS_TO_KPH
|
KPH_TO_MS = 1 / MS_TO_KPH
|
||||||
|
KPH_TO_MPH = 0.621371
|
||||||
|
MPH_TO_KPH = 1 / KPH_TO_MPH
|
||||||
|
|
||||||
INHG_TO_HPA = 33.86389
|
INHG_TO_HPA = 33.86389
|
||||||
INHG_TO_MMHG = 25.400002776728
|
INHG_TO_MMHG = 25.400002776728
|
||||||
|
|
||||||
LBS_TO_KG = 0.453592
|
LBS_TO_KG = 0.453592
|
||||||
|
KG_TO_LBS = 1 / LBS_TO_KG
|
||||||
|
|
||||||
|
|
||||||
|
class UnitSystem(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def distance_short(self, dist: Distance) -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def distance_long(self, dist: Distance) -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def distance_short_uom(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def distance_long_uom(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def speed(self, speed: Speed) -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def speed_uom(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def mass(self, mass: Mass) -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def mass_uom(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NauticalUnits(UnitSystem):
|
||||||
|
def distance_short(self, dist: Distance) -> float:
|
||||||
|
return dist.feet
|
||||||
|
|
||||||
|
def distance_long(self, dist: Distance) -> float:
|
||||||
|
return dist.nautical_miles
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance_short_uom(self) -> str:
|
||||||
|
return "ft"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance_long_uom(self) -> str:
|
||||||
|
return "nm"
|
||||||
|
|
||||||
|
def speed(self, speed: Speed) -> float:
|
||||||
|
return speed.knots
|
||||||
|
|
||||||
|
@property
|
||||||
|
def speed_uom(self) -> str:
|
||||||
|
return "kt"
|
||||||
|
|
||||||
|
def mass(self, mass: Mass) -> float:
|
||||||
|
return mass.pounds
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mass_uom(self) -> str:
|
||||||
|
return "lb"
|
||||||
|
|
||||||
|
|
||||||
|
class MetricUnits(UnitSystem):
|
||||||
|
def distance_short(self, dist: Distance) -> float:
|
||||||
|
return dist.meters
|
||||||
|
|
||||||
|
def distance_long(self, dist: Distance) -> float:
|
||||||
|
return dist.kilometers
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance_short_uom(self) -> str:
|
||||||
|
return "m"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance_long_uom(self) -> str:
|
||||||
|
return "km"
|
||||||
|
|
||||||
|
def speed(self, speed: Speed) -> float:
|
||||||
|
return speed.kph
|
||||||
|
|
||||||
|
@property
|
||||||
|
def speed_uom(self) -> str:
|
||||||
|
return "kph"
|
||||||
|
|
||||||
|
def mass(self, mass: Mass) -> float:
|
||||||
|
return mass.kgs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mass_uom(self) -> str:
|
||||||
|
return "kg"
|
||||||
|
|
||||||
|
|
||||||
|
class ImperialUnits(UnitSystem):
|
||||||
|
def distance_short(self, dist: Distance) -> float:
|
||||||
|
return dist.feet
|
||||||
|
|
||||||
|
def distance_long(self, dist: Distance) -> float:
|
||||||
|
return dist.miles
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance_short_uom(self) -> str:
|
||||||
|
return "ft"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance_long_uom(self) -> str:
|
||||||
|
return "m"
|
||||||
|
|
||||||
|
def speed(self, speed: Speed) -> float:
|
||||||
|
return speed.mph
|
||||||
|
|
||||||
|
@property
|
||||||
|
def speed_uom(self) -> str:
|
||||||
|
return "mph"
|
||||||
|
|
||||||
|
def mass(self, mass: Mass) -> float:
|
||||||
|
return mass.pounds
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mass_uom(self) -> str:
|
||||||
|
return "lb"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -42,6 +176,14 @@ class Distance:
|
|||||||
def nautical_miles(self) -> float:
|
def nautical_miles(self) -> float:
|
||||||
return self.distance_in_meters * METERS_TO_NM
|
return self.distance_in_meters * METERS_TO_NM
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kilometers(self) -> float:
|
||||||
|
return self.distance_in_meters / 1000
|
||||||
|
|
||||||
|
@property
|
||||||
|
def miles(self) -> float:
|
||||||
|
return self.distance_in_meters * METERS_TO_MILES
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_feet(cls, value: float) -> Distance:
|
def from_feet(cls, value: float) -> Distance:
|
||||||
return cls(value * FEET_TO_METERS)
|
return cls(value * FEET_TO_METERS)
|
||||||
@ -119,6 +261,10 @@ class Speed:
|
|||||||
def meters_per_second(self) -> float:
|
def meters_per_second(self) -> float:
|
||||||
return self.speed_in_kph * KPH_TO_MS
|
return self.speed_in_kph * KPH_TO_MS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mph(self) -> float:
|
||||||
|
return self.speed_in_kph * KPH_TO_MPH
|
||||||
|
|
||||||
def mach(self, altitude: Distance = meters(0)) -> float:
|
def mach(self, altitude: Distance = meters(0)) -> float:
|
||||||
c_sound = mach(1, altitude)
|
c_sound = mach(1, altitude)
|
||||||
return self.speed_in_kph / c_sound.kph
|
return self.speed_in_kph / c_sound.kph
|
||||||
@ -272,6 +418,27 @@ def inches_hg(value: float) -> Pressure:
|
|||||||
return Pressure(value)
|
return Pressure(value)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, order=True)
|
||||||
|
class Mass:
|
||||||
|
mass_in_kg: float
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pounds(self) -> float:
|
||||||
|
return self.mass_in_kg * KG_TO_LBS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kgs(self) -> float:
|
||||||
|
return self.mass_in_kg
|
||||||
|
|
||||||
|
|
||||||
|
def pounds(value: float) -> Mass:
|
||||||
|
return Mass(value * LBS_TO_KG)
|
||||||
|
|
||||||
|
|
||||||
|
def kgs(value: float) -> Mass:
|
||||||
|
return Mass(value)
|
||||||
|
|
||||||
|
|
||||||
PairwiseT = TypeVar("PairwiseT")
|
PairwiseT = TypeVar("PairwiseT")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -29,3 +29,4 @@ radios:
|
|||||||
channels:
|
channels:
|
||||||
type: viggen
|
type: viggen
|
||||||
namer: viggen
|
namer: viggen
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -17,3 +17,4 @@ role: Fighter
|
|||||||
gunfighter: true
|
gunfighter: true
|
||||||
variants:
|
variants:
|
||||||
"Bf 109 K-4 Kurf\xFCrst": {}
|
"Bf 109 K-4 Kurf\xFCrst": {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -28,3 +28,4 @@ role: Fighter
|
|||||||
gunfighter: true
|
gunfighter: true
|
||||||
variants:
|
variants:
|
||||||
Fw 190 A-8 Anton: {}
|
Fw 190 A-8 Anton: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -17,3 +17,4 @@ role: Fighter
|
|||||||
gunfighter: true
|
gunfighter: true
|
||||||
variants:
|
variants:
|
||||||
Fw 190 D-9 Dora: {}
|
Fw 190 D-9 Dora: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -10,3 +10,4 @@ price: 22
|
|||||||
role: Air-Superiority Fighter
|
role: Air-Superiority Fighter
|
||||||
variants:
|
variants:
|
||||||
J-11A Flanker-L: {}
|
J-11A Flanker-L: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -20,3 +20,4 @@ radios:
|
|||||||
# The R-800L1 doesn't have preset channels, and the other radio is for
|
# The R-800L1 doesn't have preset channels, and the other radio is for
|
||||||
# communications with FAC and ground units, which don't currently have
|
# communications with FAC and ground units, which don't currently have
|
||||||
# radios assigned, so no channels to configure.
|
# radios assigned, so no channels to configure.
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -12,3 +12,4 @@ role: Light Attack
|
|||||||
gunfighter: true
|
gunfighter: true
|
||||||
variants:
|
variants:
|
||||||
L-39ZA Albatros: {}
|
L-39ZA Albatros: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -19,6 +19,6 @@ manufacturer: Mil
|
|||||||
origin: USSR/Russia
|
origin: USSR/Russia
|
||||||
price: 14
|
price: 14
|
||||||
role: Attack/Transport
|
role: Attack/Transport
|
||||||
metric_kneeboard: true
|
kneeboard_units: "metric"
|
||||||
variants:
|
variants:
|
||||||
Mi-24P Hind-F: {}
|
Mi-24P Hind-F: {}
|
||||||
|
|||||||
@ -10,3 +10,4 @@ price: 5
|
|||||||
role: Transport/Light Attack
|
role: Transport/Light Attack
|
||||||
variants:
|
variants:
|
||||||
Mi-8MTV2 Hip: {}
|
Mi-8MTV2 Hip: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -21,3 +21,4 @@ variants:
|
|||||||
radios:
|
radios:
|
||||||
intra_flight: RSI-6K HF
|
intra_flight: RSI-6K HF
|
||||||
inter_flight: RSI-6K HF
|
inter_flight: RSI-6K HF
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -27,3 +27,4 @@ radios:
|
|||||||
channels:
|
channels:
|
||||||
type: farmer
|
type: farmer
|
||||||
namer: single
|
namer: single
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -27,3 +27,4 @@ radios:
|
|||||||
namer: single
|
namer: single
|
||||||
intra_flight_radio_index: 1
|
intra_flight_radio_index: 1
|
||||||
inter_flight_radio_index: 1
|
inter_flight_radio_index: 1
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -28,3 +28,4 @@ role: Multirole Fighter
|
|||||||
max_range: 150
|
max_range: 150
|
||||||
variants:
|
variants:
|
||||||
MiG-29A Fulcrum-A: {}
|
MiG-29A Fulcrum-A: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -28,3 +28,4 @@ role: Multirole Fighter
|
|||||||
max_range: 150
|
max_range: 150
|
||||||
variants:
|
variants:
|
||||||
MiG-29G Fulcrum-A: {}
|
MiG-29G Fulcrum-A: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -28,3 +28,4 @@ role: Multirole Fighter
|
|||||||
max_range: 150
|
max_range: 150
|
||||||
variants:
|
variants:
|
||||||
MiG-29S Fulcrum-C: {}
|
MiG-29S Fulcrum-C: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -6,3 +6,4 @@ price: 6
|
|||||||
role: Light Bomber, Fighter Bomber, Night Fighter, Maritime Strike Aircraft, Photo Recon Aircraft
|
role: Light Bomber, Fighter Bomber, Night Fighter, Maritime Strike Aircraft, Photo Recon Aircraft
|
||||||
variants:
|
variants:
|
||||||
MosquitoFBMkVI: {}
|
MosquitoFBMkVI: {}
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -29,3 +29,4 @@ radios:
|
|||||||
channels:
|
channels:
|
||||||
type: SCR-522
|
type: SCR-522
|
||||||
namer: SCR-522
|
namer: SCR-522
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -29,3 +29,4 @@ radios:
|
|||||||
channels:
|
channels:
|
||||||
type: SCR-522
|
type: SCR-522
|
||||||
namer: SCR-522
|
namer: SCR-522
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -28,4 +28,5 @@ radios:
|
|||||||
inter_flight: SCR522
|
inter_flight: SCR522
|
||||||
channels:
|
channels:
|
||||||
type: SCR-522
|
type: SCR-522
|
||||||
namer: SCR-522
|
namer: SCR-522
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -30,3 +30,4 @@ radios:
|
|||||||
channels:
|
channels:
|
||||||
type: SCR-522
|
type: SCR-522
|
||||||
namer: SCR-522
|
namer: SCR-522
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -30,3 +30,4 @@ radios:
|
|||||||
channels:
|
channels:
|
||||||
type: SCR-522
|
type: SCR-522
|
||||||
namer: SCR-522
|
namer: SCR-522
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -15,3 +15,4 @@ price: 5
|
|||||||
role: Light Attack
|
role: Light Attack
|
||||||
variants:
|
variants:
|
||||||
SA 342L Gazelle: {}
|
SA 342L Gazelle: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -18,3 +18,4 @@ variants:
|
|||||||
introduced: 1974
|
introduced: 1974
|
||||||
manufacturer: Westland
|
manufacturer: Westland
|
||||||
SA 342M Gazelle: {}
|
SA 342M Gazelle: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -1,3 +1,4 @@
|
|||||||
price: 4
|
price: 4
|
||||||
variants:
|
variants:
|
||||||
SA342Minigun: null
|
SA342Minigun: null
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -15,3 +15,4 @@ price: 8
|
|||||||
role: Light Attack
|
role: Light Attack
|
||||||
variants:
|
variants:
|
||||||
SA 342M Gazelle Mistral: {}
|
SA 342M Gazelle Mistral: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -41,3 +41,4 @@ role: Fighter
|
|||||||
gunfighter: true
|
gunfighter: true
|
||||||
variants:
|
variants:
|
||||||
Spitfire LF Mk IX: {}
|
Spitfire LF Mk IX: {}
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -41,3 +41,4 @@ role: Fighter
|
|||||||
gunfighter: true
|
gunfighter: true
|
||||||
variants:
|
variants:
|
||||||
Spitfire LF Mk IX (Clipped Wings): {}
|
Spitfire LF Mk IX (Clipped Wings): {}
|
||||||
|
kneeboard_units: "imperial"
|
||||||
@ -14,3 +14,4 @@ role: Close Air Support/Attack
|
|||||||
max_range: 200
|
max_range: 200
|
||||||
variants:
|
variants:
|
||||||
Su-25 Frogfoot: {}
|
Su-25 Frogfoot: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -14,3 +14,4 @@ role: Close Air Support/Attack
|
|||||||
max_range: 200
|
max_range: 200
|
||||||
variants:
|
variants:
|
||||||
Su-25T Frogfoot: {}
|
Su-25T Frogfoot: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -17,3 +17,4 @@ role: Air-Superiority Fighter
|
|||||||
max_range: 300
|
max_range: 300
|
||||||
variants:
|
variants:
|
||||||
Su-27 Flanker-B: {}
|
Su-27 Flanker-B: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
@ -29,3 +29,4 @@ variants:
|
|||||||
origin: China
|
origin: China
|
||||||
role: Carrier-based Multirole Fighter
|
role: Carrier-based Multirole Fighter
|
||||||
Su-33 Flanker-D: {}
|
Su-33 Flanker-D: {}
|
||||||
|
kneeboard_units: "metric"
|
||||||
Loading…
x
Reference in New Issue
Block a user