mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Convert to new unit APIs, remove old APIs.
There are probably plenty of raw ints around that never used the old conversion APIs, but we'll just need to fix those when we see them. Fixes https://github.com/Khopa/dcs_liberation/issues/558
This commit is contained in:
parent
113947b9f0
commit
2ac818dcdd
@ -1,7 +1,7 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from game.utils import Distance, feet, nm_to_meter, feet_to_meter
|
from game.utils import Distance, feet, nautical_miles
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -12,31 +12,28 @@ class Doctrine:
|
|||||||
strike: bool
|
strike: bool
|
||||||
antiship: bool
|
antiship: bool
|
||||||
|
|
||||||
strike_max_range: int
|
|
||||||
sead_max_range: int
|
|
||||||
|
|
||||||
rendezvous_altitude: Distance
|
rendezvous_altitude: Distance
|
||||||
hold_distance: int
|
hold_distance: Distance
|
||||||
push_distance: int
|
push_distance: Distance
|
||||||
join_distance: int
|
join_distance: Distance
|
||||||
split_distance: int
|
split_distance: Distance
|
||||||
ingress_egress_distance: int
|
ingress_egress_distance: Distance
|
||||||
ingress_altitude: Distance
|
ingress_altitude: Distance
|
||||||
egress_altitude: int
|
egress_altitude: Distance
|
||||||
|
|
||||||
min_patrol_altitude: int
|
min_patrol_altitude: Distance
|
||||||
max_patrol_altitude: int
|
max_patrol_altitude: Distance
|
||||||
pattern_altitude: int
|
pattern_altitude: Distance
|
||||||
|
|
||||||
cap_duration: timedelta
|
cap_duration: timedelta
|
||||||
cap_min_track_length: int
|
cap_min_track_length: Distance
|
||||||
cap_max_track_length: int
|
cap_max_track_length: Distance
|
||||||
cap_min_distance_from_cp: int
|
cap_min_distance_from_cp: Distance
|
||||||
cap_max_distance_from_cp: int
|
cap_max_distance_from_cp: Distance
|
||||||
|
|
||||||
cas_duration: timedelta
|
cas_duration: timedelta
|
||||||
|
|
||||||
sweep_distance: int
|
sweep_distance: Distance
|
||||||
|
|
||||||
|
|
||||||
MODERN_DOCTRINE = Doctrine(
|
MODERN_DOCTRINE = Doctrine(
|
||||||
@ -45,26 +42,24 @@ MODERN_DOCTRINE = Doctrine(
|
|||||||
sead=True,
|
sead=True,
|
||||||
strike=True,
|
strike=True,
|
||||||
antiship=True,
|
antiship=True,
|
||||||
strike_max_range=1500000,
|
|
||||||
sead_max_range=1500000,
|
|
||||||
rendezvous_altitude=feet(25000),
|
rendezvous_altitude=feet(25000),
|
||||||
hold_distance=nm_to_meter(15),
|
hold_distance=nautical_miles(15),
|
||||||
push_distance=nm_to_meter(20),
|
push_distance=nautical_miles(20),
|
||||||
join_distance=nm_to_meter(20),
|
join_distance=nautical_miles(20),
|
||||||
split_distance=nm_to_meter(20),
|
split_distance=nautical_miles(20),
|
||||||
ingress_egress_distance=nm_to_meter(45),
|
ingress_egress_distance=nautical_miles(45),
|
||||||
ingress_altitude=feet(20000),
|
ingress_altitude=feet(20000),
|
||||||
egress_altitude=feet_to_meter(20000),
|
egress_altitude=feet(20000),
|
||||||
min_patrol_altitude=feet_to_meter(15000),
|
min_patrol_altitude=feet(15000),
|
||||||
max_patrol_altitude=feet_to_meter(33000),
|
max_patrol_altitude=feet(33000),
|
||||||
pattern_altitude=feet_to_meter(5000),
|
pattern_altitude=feet(5000),
|
||||||
cap_duration=timedelta(minutes=30),
|
cap_duration=timedelta(minutes=30),
|
||||||
cap_min_track_length=nm_to_meter(15),
|
cap_min_track_length=nautical_miles(15),
|
||||||
cap_max_track_length=nm_to_meter(40),
|
cap_max_track_length=nautical_miles(40),
|
||||||
cap_min_distance_from_cp=nm_to_meter(10),
|
cap_min_distance_from_cp=nautical_miles(10),
|
||||||
cap_max_distance_from_cp=nm_to_meter(40),
|
cap_max_distance_from_cp=nautical_miles(40),
|
||||||
cas_duration=timedelta(minutes=30),
|
cas_duration=timedelta(minutes=30),
|
||||||
sweep_distance=nm_to_meter(60),
|
sweep_distance=nautical_miles(60),
|
||||||
)
|
)
|
||||||
|
|
||||||
COLDWAR_DOCTRINE = Doctrine(
|
COLDWAR_DOCTRINE = Doctrine(
|
||||||
@ -73,26 +68,24 @@ COLDWAR_DOCTRINE = Doctrine(
|
|||||||
sead=True,
|
sead=True,
|
||||||
strike=True,
|
strike=True,
|
||||||
antiship=True,
|
antiship=True,
|
||||||
strike_max_range=1500000,
|
|
||||||
sead_max_range=1500000,
|
|
||||||
rendezvous_altitude=feet(22000),
|
rendezvous_altitude=feet(22000),
|
||||||
hold_distance=nm_to_meter(10),
|
hold_distance=nautical_miles(10),
|
||||||
push_distance=nm_to_meter(10),
|
push_distance=nautical_miles(10),
|
||||||
join_distance=nm_to_meter(10),
|
join_distance=nautical_miles(10),
|
||||||
split_distance=nm_to_meter(10),
|
split_distance=nautical_miles(10),
|
||||||
ingress_egress_distance=nm_to_meter(30),
|
ingress_egress_distance=nautical_miles(30),
|
||||||
ingress_altitude=feet(18000),
|
ingress_altitude=feet(18000),
|
||||||
egress_altitude=feet_to_meter(18000),
|
egress_altitude=feet(18000),
|
||||||
min_patrol_altitude=feet_to_meter(10000),
|
min_patrol_altitude=feet(10000),
|
||||||
max_patrol_altitude=feet_to_meter(24000),
|
max_patrol_altitude=feet(24000),
|
||||||
pattern_altitude=feet_to_meter(5000),
|
pattern_altitude=feet(5000),
|
||||||
cap_duration=timedelta(minutes=30),
|
cap_duration=timedelta(minutes=30),
|
||||||
cap_min_track_length=nm_to_meter(12),
|
cap_min_track_length=nautical_miles(12),
|
||||||
cap_max_track_length=nm_to_meter(24),
|
cap_max_track_length=nautical_miles(24),
|
||||||
cap_min_distance_from_cp=nm_to_meter(8),
|
cap_min_distance_from_cp=nautical_miles(8),
|
||||||
cap_max_distance_from_cp=nm_to_meter(25),
|
cap_max_distance_from_cp=nautical_miles(25),
|
||||||
cas_duration=timedelta(minutes=30),
|
cas_duration=timedelta(minutes=30),
|
||||||
sweep_distance=nm_to_meter(40),
|
sweep_distance=nautical_miles(40),
|
||||||
)
|
)
|
||||||
|
|
||||||
WWII_DOCTRINE = Doctrine(
|
WWII_DOCTRINE = Doctrine(
|
||||||
@ -101,24 +94,22 @@ WWII_DOCTRINE = Doctrine(
|
|||||||
sead=False,
|
sead=False,
|
||||||
strike=True,
|
strike=True,
|
||||||
antiship=True,
|
antiship=True,
|
||||||
strike_max_range=1500000,
|
hold_distance=nautical_miles(5),
|
||||||
sead_max_range=1500000,
|
push_distance=nautical_miles(5),
|
||||||
hold_distance=nm_to_meter(5),
|
join_distance=nautical_miles(5),
|
||||||
push_distance=nm_to_meter(5),
|
split_distance=nautical_miles(5),
|
||||||
join_distance=nm_to_meter(5),
|
|
||||||
split_distance=nm_to_meter(5),
|
|
||||||
rendezvous_altitude=feet(10000),
|
rendezvous_altitude=feet(10000),
|
||||||
ingress_egress_distance=nm_to_meter(7),
|
ingress_egress_distance=nautical_miles(7),
|
||||||
ingress_altitude=feet(8000),
|
ingress_altitude=feet(8000),
|
||||||
egress_altitude=feet_to_meter(8000),
|
egress_altitude=feet(8000),
|
||||||
min_patrol_altitude=feet_to_meter(4000),
|
min_patrol_altitude=feet(4000),
|
||||||
max_patrol_altitude=feet_to_meter(15000),
|
max_patrol_altitude=feet(15000),
|
||||||
pattern_altitude=feet_to_meter(5000),
|
pattern_altitude=feet(5000),
|
||||||
cap_duration=timedelta(minutes=30),
|
cap_duration=timedelta(minutes=30),
|
||||||
cap_min_track_length=nm_to_meter(8),
|
cap_min_track_length=nautical_miles(8),
|
||||||
cap_max_track_length=nm_to_meter(18),
|
cap_max_track_length=nautical_miles(18),
|
||||||
cap_min_distance_from_cp=nm_to_meter(0),
|
cap_min_distance_from_cp=nautical_miles(0),
|
||||||
cap_max_distance_from_cp=nm_to_meter(5),
|
cap_max_distance_from_cp=nautical_miles(5),
|
||||||
cas_duration=timedelta(minutes=30),
|
cas_duration=timedelta(minutes=30),
|
||||||
sweep_distance=nm_to_meter(10),
|
sweep_distance=nautical_miles(10),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,11 +6,12 @@ import random
|
|||||||
from typing import Iterator, List, Optional, TYPE_CHECKING, Type
|
from typing import Iterator, List, Optional, TYPE_CHECKING, Type
|
||||||
|
|
||||||
from dcs.task import CAP, CAS
|
from dcs.task import CAP, CAS
|
||||||
from dcs.unittype import FlyingType, UnitType, VehicleType
|
from dcs.unittype import FlyingType, VehicleType
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
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 gen.flights.ai_flight_planner_db import (
|
from gen.flights.ai_flight_planner_db import (
|
||||||
capable_aircraft_for_task,
|
capable_aircraft_for_task,
|
||||||
preferred_aircraft_for_task,
|
preferred_aircraft_for_task,
|
||||||
@ -25,7 +26,7 @@ if TYPE_CHECKING:
|
|||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AircraftProcurementRequest:
|
class AircraftProcurementRequest:
|
||||||
near: MissionTarget
|
near: MissionTarget
|
||||||
range: int
|
range: Distance
|
||||||
task_capability: FlightType
|
task_capability: FlightType
|
||||||
number: int
|
number: int
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,7 @@ from .controlpoint import (
|
|||||||
Fob,
|
Fob,
|
||||||
)
|
)
|
||||||
from .landmap import Landmap, load_landmap, poly_contains
|
from .landmap import Landmap, load_landmap, poly_contains
|
||||||
from ..utils import nm_to_meter
|
from ..utils import Distance, meters, nautical_miles
|
||||||
|
|
||||||
Numeric = Union[int, float]
|
Numeric = Union[int, float]
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ class MizCampaignLoader:
|
|||||||
AirDefence.SAM_SA_3_S_125_LN_5P73.id,
|
AirDefence.SAM_SA_3_S_125_LN_5P73.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
BASE_DEFENSE_RADIUS = nm_to_meter(2)
|
BASE_DEFENSE_RADIUS = nautical_miles(2)
|
||||||
|
|
||||||
def __init__(self, miz: Path, theater: ConflictTheater) -> None:
|
def __init__(self, miz: Path, theater: ConflictTheater) -> None:
|
||||||
self.theater = theater
|
self.theater = theater
|
||||||
@ -317,9 +317,9 @@ class MizCampaignLoader:
|
|||||||
self.control_points[origin.id])
|
self.control_points[origin.id])
|
||||||
return front_lines
|
return front_lines
|
||||||
|
|
||||||
def objective_info(self, group: Group) -> Tuple[ControlPoint, int]:
|
def objective_info(self, group: Group) -> Tuple[ControlPoint, Distance]:
|
||||||
closest = self.theater.closest_control_point(group.position)
|
closest = self.theater.closest_control_point(group.position)
|
||||||
distance = closest.position.distance_to_point(group.position)
|
distance = meters(closest.position.distance_to_point(group.position))
|
||||||
return closest, distance
|
return closest, distance
|
||||||
|
|
||||||
def add_preset_locations(self) -> None:
|
def add_preset_locations(self) -> None:
|
||||||
|
|||||||
@ -29,38 +29,6 @@ def opposite_heading(h):
|
|||||||
return heading_sum(h, 180)
|
return heading_sum(h, 180)
|
||||||
|
|
||||||
|
|
||||||
def meter_to_feet(value: float) -> int:
|
|
||||||
return int(meters(value).feet)
|
|
||||||
|
|
||||||
|
|
||||||
def feet_to_meter(value: float) -> int:
|
|
||||||
return int(feet(value).meters)
|
|
||||||
|
|
||||||
|
|
||||||
def meter_to_nm(value: float) -> int:
|
|
||||||
return int(meters(value).nautical_miles)
|
|
||||||
|
|
||||||
|
|
||||||
def nm_to_meter(value: float) -> int:
|
|
||||||
return int(nautical_miles(value).meters)
|
|
||||||
|
|
||||||
|
|
||||||
def knots_to_kph(value: float) -> int:
|
|
||||||
return int(knots(value).kph)
|
|
||||||
|
|
||||||
|
|
||||||
def kph_to_mps(value: float) -> int:
|
|
||||||
return int(kph(value).meters_per_second)
|
|
||||||
|
|
||||||
|
|
||||||
def mps_to_kph(value: float) -> int:
|
|
||||||
return int(mps(value).kph)
|
|
||||||
|
|
||||||
|
|
||||||
def mps_to_knots(value: float) -> int:
|
|
||||||
return int(mps(value).knots)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, order=True)
|
@dataclass(frozen=True, order=True)
|
||||||
class Distance:
|
class Distance:
|
||||||
distance_in_meters: float
|
distance_in_meters: float
|
||||||
@ -89,14 +57,20 @@ class Distance:
|
|||||||
def from_nautical_miles(cls, value: float) -> Distance:
|
def from_nautical_miles(cls, value: float) -> Distance:
|
||||||
return cls(value * NM_TO_METERS)
|
return cls(value * NM_TO_METERS)
|
||||||
|
|
||||||
|
def __add__(self, other: Distance) -> Distance:
|
||||||
|
return meters(self.meters + other.meters)
|
||||||
|
|
||||||
|
def __sub__(self, other: Distance) -> Distance:
|
||||||
|
return meters(self.meters - other.meters)
|
||||||
|
|
||||||
def __mul__(self, other: Union[float, int]) -> Distance:
|
def __mul__(self, other: Union[float, int]) -> Distance:
|
||||||
return Distance(self.meters * other)
|
return meters(self.meters * other)
|
||||||
|
|
||||||
def __truediv__(self, other: Union[float, int]) -> Distance:
|
def __truediv__(self, other: Union[float, int]) -> Distance:
|
||||||
return Distance(self.meters / other)
|
return meters(self.meters / other)
|
||||||
|
|
||||||
def __floordiv__(self, other: Union[float, int]) -> Distance:
|
def __floordiv__(self, other: Union[float, int]) -> Distance:
|
||||||
return Distance(self.meters // other)
|
return meters(self.meters // other)
|
||||||
|
|
||||||
|
|
||||||
def feet(value: float) -> Distance:
|
def feet(value: float) -> Distance:
|
||||||
@ -165,14 +139,20 @@ class Speed:
|
|||||||
c_sound = math.sqrt(heat_capacity_ratio * gas_constant * temperature_k)
|
c_sound = math.sqrt(heat_capacity_ratio * gas_constant * temperature_k)
|
||||||
return mps(c_sound) * value
|
return mps(c_sound) * value
|
||||||
|
|
||||||
|
def __add__(self, other: Speed) -> Speed:
|
||||||
|
return kph(self.kph + other.kph)
|
||||||
|
|
||||||
|
def __sub__(self, other: Speed) -> Speed:
|
||||||
|
return kph(self.kph - other.kph)
|
||||||
|
|
||||||
def __mul__(self, other: Union[float, int]) -> Speed:
|
def __mul__(self, other: Union[float, int]) -> Speed:
|
||||||
return Speed(self.kph * other)
|
return kph(self.kph * other)
|
||||||
|
|
||||||
def __truediv__(self, other: Union[float, int]) -> Speed:
|
def __truediv__(self, other: Union[float, int]) -> Speed:
|
||||||
return Speed(self.kph / other)
|
return kph(self.kph / other)
|
||||||
|
|
||||||
def __floordiv__(self, other: Union[float, int]) -> Speed:
|
def __floordiv__(self, other: Union[float, int]) -> Speed:
|
||||||
return Speed(self.kph // other)
|
return kph(self.kph // other)
|
||||||
|
|
||||||
|
|
||||||
def knots(value: float) -> Speed:
|
def knots(value: float) -> Speed:
|
||||||
|
|||||||
@ -10,6 +10,7 @@ from typing import Optional, TYPE_CHECKING
|
|||||||
from dcs.weather import Weather as PydcsWeather, Wind
|
from dcs.weather import Weather as PydcsWeather, Wind
|
||||||
|
|
||||||
from game.settings import Settings
|
from game.settings import Settings
|
||||||
|
from game.utils import Distance, meters
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.theater import ConflictTheater
|
from game.theater import ConflictTheater
|
||||||
@ -39,7 +40,7 @@ class Clouds:
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Fog:
|
class Fog:
|
||||||
visibility: int
|
visibility: Distance
|
||||||
thickness: int
|
thickness: int
|
||||||
|
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ class Weather:
|
|||||||
if random.randrange(5) != 0:
|
if random.randrange(5) != 0:
|
||||||
return None
|
return None
|
||||||
return Fog(
|
return Fog(
|
||||||
visibility=random.randint(2500, 5000),
|
visibility=meters(random.randint(2500, 5000)),
|
||||||
thickness=random.randint(100, 500)
|
thickness=random.randint(100, 500)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -85,7 +85,7 @@ from game.theater.controlpoint import (
|
|||||||
)
|
)
|
||||||
from game.theater.theatergroundobject import TheaterGroundObject
|
from game.theater.theatergroundobject import TheaterGroundObject
|
||||||
from game.unitmap import UnitMap
|
from game.unitmap import UnitMap
|
||||||
from game.utils import Distance, Speed, knots_to_kph, kph, meters, nm_to_meter
|
from game.utils import Distance, meters, nautical_miles
|
||||||
from gen.airsupportgen import AirSupport
|
from gen.airsupportgen import AirSupport
|
||||||
from gen.ato import AirTaskingOrder, Package
|
from gen.ato import AirTaskingOrder, Package
|
||||||
from gen.callsigns import create_group_callsign_from_unit
|
from gen.callsigns import create_group_callsign_from_unit
|
||||||
@ -110,10 +110,8 @@ from .naming import namegen
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game import Game
|
from game import Game
|
||||||
|
|
||||||
WARM_START_HELI_AIRSPEED = kph(120)
|
|
||||||
WARM_START_HELI_ALT = meters(500)
|
WARM_START_HELI_ALT = meters(500)
|
||||||
WARM_START_ALTITUDE = meters(3000)
|
WARM_START_ALTITUDE = meters(3000)
|
||||||
WARM_START_AIRSPEED = kph(550)
|
|
||||||
|
|
||||||
RTB_ALTITUDE = meters(800)
|
RTB_ALTITUDE = meters(800)
|
||||||
RTB_DISTANCE = 5000
|
RTB_DISTANCE = 5000
|
||||||
@ -832,11 +830,13 @@ class AircraftConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
alt = WARM_START_ALTITUDE
|
alt = WARM_START_ALTITUDE
|
||||||
|
|
||||||
speed = knots_to_kph(GroundSpeed.for_flight(flight, alt))
|
speed = GroundSpeed.for_flight(flight, alt)
|
||||||
|
|
||||||
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
||||||
|
|
||||||
logging.info("airgen: {} for {} at {} at {}".format(flight.unit_type, side.id, alt, speed))
|
logging.info(
|
||||||
|
"airgen: {} for {} at {} at {}".format(flight.unit_type, side.id,
|
||||||
|
alt, int(speed.kph)))
|
||||||
group = self.m.flight_group(
|
group = self.m.flight_group(
|
||||||
country=side,
|
country=side,
|
||||||
name=name,
|
name=name,
|
||||||
@ -844,7 +844,7 @@ class AircraftConflictGenerator:
|
|||||||
airport=None,
|
airport=None,
|
||||||
position=pos,
|
position=pos,
|
||||||
altitude=alt,
|
altitude=alt,
|
||||||
speed=speed,
|
speed=speed.kph,
|
||||||
maintask=None,
|
maintask=None,
|
||||||
group_size=flight.count)
|
group_size=flight.count)
|
||||||
|
|
||||||
@ -1515,7 +1515,7 @@ class CasIngressBuilder(PydcsWaypointBuilder):
|
|||||||
logging.error(
|
logging.error(
|
||||||
"No CAS waypoint found. Falling back to search and engage")
|
"No CAS waypoint found. Falling back to search and engage")
|
||||||
waypoint.add_task(EngageTargets(
|
waypoint.add_task(EngageTargets(
|
||||||
max_distance=nm_to_meter(10),
|
max_distance=int(nautical_miles(10).meters),
|
||||||
targets=[
|
targets=[
|
||||||
Targets.All.GroundUnits.GroundVehicles,
|
Targets.All.GroundUnits.GroundVehicles,
|
||||||
Targets.All.GroundUnits.AirDefence.AAA,
|
Targets.All.GroundUnits.AirDefence.AAA,
|
||||||
@ -1564,7 +1564,7 @@ class OcaAircraftIngressBuilder(PydcsWaypointBuilder):
|
|||||||
position=target.position,
|
position=target.position,
|
||||||
# Al Dhafra is 4 nm across at most. Add a little wiggle room in case
|
# Al Dhafra is 4 nm across at most. Add a little wiggle room in case
|
||||||
# the airport position from DCS is not centered.
|
# the airport position from DCS is not centered.
|
||||||
radius=nm_to_meter(3),
|
radius=int(nautical_miles(3).meters),
|
||||||
targets=[Targets.All.Air]
|
targets=[Targets.All.Air]
|
||||||
)
|
)
|
||||||
task.params["attackQtyLimit"] = False
|
task.params["attackQtyLimit"] = False
|
||||||
@ -1604,7 +1604,7 @@ class SeadIngressBuilder(PydcsWaypointBuilder):
|
|||||||
if tgroup is not None:
|
if tgroup is not None:
|
||||||
waypoint.add_task(EngageTargetsInZone(
|
waypoint.add_task(EngageTargetsInZone(
|
||||||
position=tgroup.position,
|
position=tgroup.position,
|
||||||
radius=nm_to_meter(30),
|
radius=int(nautical_miles(30).meters),
|
||||||
targets=[
|
targets=[
|
||||||
Targets.All.GroundUnits.AirDefence,
|
Targets.All.GroundUnits.AirDefence,
|
||||||
])
|
])
|
||||||
@ -1686,7 +1686,7 @@ class SweepIngressBuilder(PydcsWaypointBuilder):
|
|||||||
return waypoint
|
return waypoint
|
||||||
|
|
||||||
waypoint.tasks.append(EngageTargets(
|
waypoint.tasks.append(EngageTargets(
|
||||||
max_distance=nm_to_meter(50),
|
max_distance=int(nautical_miles(50).meters),
|
||||||
targets=[Targets.All.Air.Planes.Fighters]))
|
targets=[Targets.All.Air.Planes.Fighters]))
|
||||||
|
|
||||||
return waypoint
|
return waypoint
|
||||||
@ -1729,7 +1729,7 @@ class JoinPointBuilder(PydcsWaypointBuilder):
|
|||||||
# https://forums.eagle.ru/forum/english/digital-combat-simulator/dcs-world-2-5/bugs-and-problems-ai/ai-ad/250183-task-follow-and-escort-temporarily-aborted
|
# https://forums.eagle.ru/forum/english/digital-combat-simulator/dcs-world-2-5/bugs-and-problems-ai/ai-ad/250183-task-follow-and-escort-temporarily-aborted
|
||||||
waypoint.add_task(ControlledTask(EngageTargets(
|
waypoint.add_task(ControlledTask(EngageTargets(
|
||||||
# TODO: From doctrine.
|
# TODO: From doctrine.
|
||||||
max_distance=nm_to_meter(30),
|
max_distance=int(nautical_miles(30).meters),
|
||||||
targets=[Targets.All.Air.Planes.Fighters]
|
targets=[Targets.All.Air.Planes.Fighters]
|
||||||
)))
|
)))
|
||||||
|
|
||||||
@ -1769,8 +1769,9 @@ class RaceTrackBuilder(PydcsWaypointBuilder):
|
|||||||
# later.
|
# later.
|
||||||
cap_types = {FlightType.BARCAP, FlightType.TARCAP}
|
cap_types = {FlightType.BARCAP, FlightType.TARCAP}
|
||||||
if self.flight.flight_type in cap_types:
|
if self.flight.flight_type in cap_types:
|
||||||
waypoint.tasks.append(EngageTargets(max_distance=nm_to_meter(50),
|
waypoint.tasks.append(
|
||||||
targets=[Targets.All.Air]))
|
EngageTargets(max_distance=int(nautical_miles(50).meters),
|
||||||
|
targets=[Targets.All.Air]))
|
||||||
|
|
||||||
racetrack = ControlledTask(OrbitAction(
|
racetrack = ControlledTask(OrbitAction(
|
||||||
altitude=waypoint.alt,
|
altitude=waypoint.alt,
|
||||||
|
|||||||
@ -17,6 +17,7 @@ from typing import Dict, List, Optional
|
|||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
|
|
||||||
from game.theater.missiontarget import MissionTarget
|
from game.theater.missiontarget import MissionTarget
|
||||||
|
from game.utils import Speed
|
||||||
from .flights.flight import Flight, FlightType
|
from .flights.flight import Flight, FlightType
|
||||||
from .flights.flightplan import FormationFlightPlan
|
from .flights.flightplan import FormationFlightPlan
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ class Package:
|
|||||||
waypoints: Optional[PackageWaypoints] = field(default=None)
|
waypoints: Optional[PackageWaypoints] = field(default=None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def formation_speed(self) -> Optional[int]:
|
def formation_speed(self) -> Optional[Speed]:
|
||||||
"""The speed of the package when in formation.
|
"""The speed of the package when in formation.
|
||||||
|
|
||||||
If none of the flights in the package will join a formation, this
|
If none of the flights in the package will join a formation, this
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class EnvironmentGenerator:
|
|||||||
def set_fog(self, fog: Optional[Fog]) -> None:
|
def set_fog(self, fog: Optional[Fog]) -> None:
|
||||||
if fog is None:
|
if fog is None:
|
||||||
return
|
return
|
||||||
self.mission.weather.fog_visibility = fog.visibility
|
self.mission.weather.fog_visibility = fog.visibility.meters
|
||||||
self.mission.weather.fog_thickness = fog.thickness
|
self.mission.weather.fog_thickness = fog.thickness
|
||||||
|
|
||||||
def set_wind(self, wind: WindConditions) -> None:
|
def set_wind(self, wind: WindConditions) -> None:
|
||||||
|
|||||||
@ -36,22 +36,12 @@ from game.theater.theatergroundobject import (
|
|||||||
EwrGroundObject,
|
EwrGroundObject,
|
||||||
NavalGroundObject, VehicleGroupGroundObject,
|
NavalGroundObject, VehicleGroupGroundObject,
|
||||||
)
|
)
|
||||||
from game.utils import nm_to_meter
|
from game.utils import Distance, nautical_miles, nautical_miles
|
||||||
from gen import Conflict
|
from gen import Conflict
|
||||||
from gen.ato import Package
|
from gen.ato import Package
|
||||||
from gen.flights.ai_flight_planner_db import (
|
from gen.flights.ai_flight_planner_db import (
|
||||||
ANTISHIP_CAPABLE,
|
capable_aircraft_for_task,
|
||||||
ANTISHIP_PREFERRED,
|
preferred_aircraft_for_task,
|
||||||
CAP_CAPABLE,
|
|
||||||
CAP_PREFERRED,
|
|
||||||
CAS_CAPABLE,
|
|
||||||
CAS_PREFERRED,
|
|
||||||
RUNWAY_ATTACK_CAPABLE,
|
|
||||||
RUNWAY_ATTACK_PREFERRED,
|
|
||||||
SEAD_CAPABLE,
|
|
||||||
SEAD_PREFERRED,
|
|
||||||
STRIKE_CAPABLE,
|
|
||||||
STRIKE_PREFERRED, capable_aircraft_for_task, preferred_aircraft_for_task,
|
|
||||||
)
|
)
|
||||||
from gen.flights.closestairfields import (
|
from gen.flights.closestairfields import (
|
||||||
ClosestAirfields,
|
ClosestAirfields,
|
||||||
@ -85,7 +75,7 @@ class ProposedFlight:
|
|||||||
num_aircraft: int
|
num_aircraft: int
|
||||||
|
|
||||||
#: The maximum distance between the objective and the departure airfield.
|
#: The maximum distance between the objective and the departure airfield.
|
||||||
max_distance: int
|
max_distance: Distance
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.task} {self.num_aircraft} ship"
|
return f"{self.task} {self.num_aircraft} ship"
|
||||||
@ -212,7 +202,7 @@ class PackageBuilder:
|
|||||||
|
|
||||||
def find_divert_field(self, aircraft: FlyingType,
|
def find_divert_field(self, aircraft: FlyingType,
|
||||||
arrival: ControlPoint) -> Optional[ControlPoint]:
|
arrival: ControlPoint) -> Optional[ControlPoint]:
|
||||||
divert_limit = nm_to_meter(150)
|
divert_limit = nautical_miles(150)
|
||||||
for airfield in self.closest_airfields.airfields_within(divert_limit):
|
for airfield in self.closest_airfields.airfields_within(divert_limit):
|
||||||
if airfield.captured != self.is_player:
|
if airfield.captured != self.is_player:
|
||||||
continue
|
continue
|
||||||
@ -241,8 +231,8 @@ class ObjectiveFinder:
|
|||||||
"""Identifies potential objectives for the mission planner."""
|
"""Identifies potential objectives for the mission planner."""
|
||||||
|
|
||||||
# TODO: Merge into doctrine.
|
# TODO: Merge into doctrine.
|
||||||
AIRFIELD_THREAT_RANGE = nm_to_meter(150)
|
AIRFIELD_THREAT_RANGE = nautical_miles(150)
|
||||||
SAM_THREAT_RANGE = nm_to_meter(100)
|
SAM_THREAT_RANGE = nautical_miles(100)
|
||||||
|
|
||||||
def __init__(self, game: Game, is_player: bool) -> None:
|
def __init__(self, game: Game, is_player: bool) -> None:
|
||||||
self.game = game
|
self.game = game
|
||||||
@ -467,13 +457,13 @@ class CoalitionMissionPlanner:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: Merge into doctrine, also limit by aircraft.
|
# TODO: Merge into doctrine, also limit by aircraft.
|
||||||
MAX_CAP_RANGE = nm_to_meter(100)
|
MAX_CAP_RANGE = nautical_miles(100)
|
||||||
MAX_CAS_RANGE = nm_to_meter(50)
|
MAX_CAS_RANGE = nautical_miles(50)
|
||||||
MAX_ANTISHIP_RANGE = nm_to_meter(150)
|
MAX_ANTISHIP_RANGE = nautical_miles(150)
|
||||||
MAX_BAI_RANGE = nm_to_meter(150)
|
MAX_BAI_RANGE = nautical_miles(150)
|
||||||
MAX_OCA_RANGE = nm_to_meter(150)
|
MAX_OCA_RANGE = nautical_miles(150)
|
||||||
MAX_SEAD_RANGE = nm_to_meter(150)
|
MAX_SEAD_RANGE = nautical_miles(150)
|
||||||
MAX_STRIKE_RANGE = nm_to_meter(150)
|
MAX_STRIKE_RANGE = nautical_miles(150)
|
||||||
|
|
||||||
def __init__(self, game: Game, is_player: bool) -> None:
|
def __init__(self, game: Game, is_player: bool) -> None:
|
||||||
self.game = game
|
self.game = game
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
from typing import Dict, Iterator, List, Optional
|
from typing import Dict, Iterator, List, Optional
|
||||||
|
|
||||||
from game.theater import ConflictTheater, ControlPoint, MissionTarget
|
from game.theater import ConflictTheater, ControlPoint, MissionTarget
|
||||||
|
from game.utils import Distance
|
||||||
|
|
||||||
|
|
||||||
class ClosestAirfields:
|
class ClosestAirfields:
|
||||||
@ -14,14 +15,14 @@ class ClosestAirfields:
|
|||||||
all_control_points, key=lambda c: self.target.distance_to(c)
|
all_control_points, key=lambda c: self.target.distance_to(c)
|
||||||
)
|
)
|
||||||
|
|
||||||
def airfields_within(self, meters: int) -> Iterator[ControlPoint]:
|
def airfields_within(self, distance: Distance) -> Iterator[ControlPoint]:
|
||||||
"""Iterates over all airfields within the given range of the target.
|
"""Iterates over all airfields within the given range of the target.
|
||||||
|
|
||||||
Note that this iterates over *all* airfields, not just friendly
|
Note that this iterates over *all* airfields, not just friendly
|
||||||
airfields.
|
airfields.
|
||||||
"""
|
"""
|
||||||
for cp in self.closest_airfields:
|
for cp in self.closest_airfields:
|
||||||
if cp.distance_to(self.target) < meters:
|
if cp.distance_to(self.target) < distance.meters:
|
||||||
yield cp
|
yield cp
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -106,7 +106,7 @@ class FlightWaypoint:
|
|||||||
def from_pydcs(cls, point: MovingPoint,
|
def from_pydcs(cls, point: MovingPoint,
|
||||||
from_cp: ControlPoint) -> "FlightWaypoint":
|
from_cp: ControlPoint) -> "FlightWaypoint":
|
||||||
waypoint = FlightWaypoint(FlightWaypointType.NAV, point.position.x,
|
waypoint = FlightWaypoint(FlightWaypointType.NAV, point.position.x,
|
||||||
point.position.y, point.alt)
|
point.position.y, meters(point.alt))
|
||||||
waypoint.alt_type = point.alt_type
|
waypoint.alt_type = point.alt_type
|
||||||
# Other actions exist... but none of them *should* be the first
|
# Other actions exist... but none of them *should* be the first
|
||||||
# waypoint for a flight.
|
# waypoint for a flight.
|
||||||
|
|||||||
@ -28,7 +28,7 @@ from game.theater import (
|
|||||||
TheaterGroundObject,
|
TheaterGroundObject,
|
||||||
)
|
)
|
||||||
from game.theater.theatergroundobject import EwrGroundObject
|
from game.theater.theatergroundobject import EwrGroundObject
|
||||||
from game.utils import Distance, meters, nm_to_meter, meter_to_nm
|
from game.utils import Distance, Speed, meters, nautical_miles
|
||||||
from .closestairfields import ObjectiveDistanceCache
|
from .closestairfields import ObjectiveDistanceCache
|
||||||
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||||
from .traveltime import GroundSpeed, TravelTime
|
from .traveltime import GroundSpeed, TravelTime
|
||||||
@ -86,7 +86,7 @@ class FlightPlan:
|
|||||||
return zip(self.waypoints[:last_index], self.waypoints[1:last_index])
|
return zip(self.waypoints[:last_index], self.waypoints[1:last_index])
|
||||||
|
|
||||||
def best_speed_between_waypoints(self, a: FlightWaypoint,
|
def best_speed_between_waypoints(self, a: FlightWaypoint,
|
||||||
b: FlightWaypoint) -> int:
|
b: FlightWaypoint) -> Speed:
|
||||||
"""Desired ground speed between points a and b."""
|
"""Desired ground speed between points a and b."""
|
||||||
factor = 1.0
|
factor = 1.0
|
||||||
if b.waypoint_type == FlightWaypointType.ASCEND_POINT:
|
if b.waypoint_type == FlightWaypointType.ASCEND_POINT:
|
||||||
@ -105,11 +105,10 @@ class FlightPlan:
|
|||||||
# We don't have an exact heightmap, but we should probably be performing
|
# We don't have an exact heightmap, but we should probably be performing
|
||||||
# *some* adjustment for NTTR since the minimum altitude of the map is
|
# *some* adjustment for NTTR since the minimum altitude of the map is
|
||||||
# near 2000 ft MSL.
|
# near 2000 ft MSL.
|
||||||
return int(
|
return GroundSpeed.for_flight(self.flight, min(a.alt, b.alt)) * factor
|
||||||
GroundSpeed.for_flight(self.flight, min(a.alt, b.alt)) * factor)
|
|
||||||
|
|
||||||
def speed_between_waypoints(self, a: FlightWaypoint,
|
def speed_between_waypoints(self, a: FlightWaypoint,
|
||||||
b: FlightWaypoint) -> int:
|
b: FlightWaypoint) -> Speed:
|
||||||
return self.best_speed_between_waypoints(a, b)
|
return self.best_speed_between_waypoints(a, b)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -126,16 +125,17 @@ class FlightPlan:
|
|||||||
def bingo_fuel(self) -> int:
|
def bingo_fuel(self) -> int:
|
||||||
"""Bingo fuel value for the FlightPlan
|
"""Bingo fuel value for the FlightPlan
|
||||||
"""
|
"""
|
||||||
distance_to_arrival = meter_to_nm(self.max_distance_from(self.flight.arrival))
|
distance_to_arrival = self.max_distance_from(self.flight.arrival)
|
||||||
|
|
||||||
bingo = 1000 # Minimum Emergency Fuel
|
bingo = 1000.0 # Minimum Emergency Fuel
|
||||||
bingo += 500 # Visual Traffic
|
bingo += 500 # Visual Traffic
|
||||||
bingo += 15 * distance_to_arrival
|
bingo += 15 * distance_to_arrival.nautical_miles
|
||||||
|
|
||||||
# TODO: Per aircraft tweaks.
|
# TODO: Per aircraft tweaks.
|
||||||
|
|
||||||
if self.flight.divert is not None:
|
if self.flight.divert is not None:
|
||||||
bingo += 10 * meter_to_nm(self.max_distance_from(self.flight.divert))
|
max_divert_distance = self.max_distance_from(self.flight.divert)
|
||||||
|
bingo += 10 * max_divert_distance.nautical_miles
|
||||||
|
|
||||||
return round(bingo / 100) * 100
|
return round(bingo / 100) * 100
|
||||||
|
|
||||||
@ -145,13 +145,14 @@ class FlightPlan:
|
|||||||
"""
|
"""
|
||||||
return self.bingo_fuel + 1000
|
return self.bingo_fuel + 1000
|
||||||
|
|
||||||
def max_distance_from(self, cp: ControlPoint) -> int:
|
def max_distance_from(self, cp: ControlPoint) -> Distance:
|
||||||
"""Returns the farthest waypoint of the flight plan from a ControlPoint.
|
"""Returns the farthest waypoint of the flight plan from a ControlPoint.
|
||||||
:arg cp The ControlPoint to measure distance from.
|
:arg cp The ControlPoint to measure distance from.
|
||||||
"""
|
"""
|
||||||
if not self.waypoints:
|
if not self.waypoints:
|
||||||
return 0
|
return meters(0)
|
||||||
return max([cp.position.distance_to_point(w.position) for w in self.waypoints])
|
return max([meters(cp.position.distance_to_point(w.position)) for w in
|
||||||
|
self.waypoints])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_offset(self) -> timedelta:
|
def tot_offset(self) -> timedelta:
|
||||||
@ -303,7 +304,7 @@ class FormationFlightPlan(LoiterFlightPlan):
|
|||||||
return self.split
|
return self.split
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def best_flight_formation_speed(self) -> int:
|
def best_flight_formation_speed(self) -> Speed:
|
||||||
"""The best speed this flight is capable at all formation waypoints.
|
"""The best speed this flight is capable at all formation waypoints.
|
||||||
|
|
||||||
To ease coordination with other flights, we aim to have a single mission
|
To ease coordination with other flights, we aim to have a single mission
|
||||||
@ -319,7 +320,7 @@ class FormationFlightPlan(LoiterFlightPlan):
|
|||||||
return min(speeds)
|
return min(speeds)
|
||||||
|
|
||||||
def speed_between_waypoints(self, a: FlightWaypoint,
|
def speed_between_waypoints(self, a: FlightWaypoint,
|
||||||
b: FlightWaypoint) -> int:
|
b: FlightWaypoint) -> Speed:
|
||||||
if b in self.package_speed_waypoints:
|
if b in self.package_speed_waypoints:
|
||||||
# Should be impossible, as any package with at least one
|
# Should be impossible, as any package with at least one
|
||||||
# FormationFlightPlan flight needs a formation speed.
|
# FormationFlightPlan flight needs a formation speed.
|
||||||
@ -519,7 +520,7 @@ class StrikeFlightPlan(FormationFlightPlan):
|
|||||||
} | set(self.targets)
|
} | set(self.targets)
|
||||||
|
|
||||||
def speed_between_waypoints(self, a: FlightWaypoint,
|
def speed_between_waypoints(self, a: FlightWaypoint,
|
||||||
b: FlightWaypoint) -> int:
|
b: FlightWaypoint) -> Speed:
|
||||||
# FlightWaypoint is only comparable by identity, so adding
|
# FlightWaypoint is only comparable by identity, so adding
|
||||||
# target_area_waypoint to package_speed_waypoints is useless.
|
# target_area_waypoint to package_speed_waypoints is useless.
|
||||||
if b.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC:
|
if b.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC:
|
||||||
@ -563,7 +564,7 @@ class StrikeFlightPlan(FormationFlightPlan):
|
|||||||
return total
|
return total
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mission_speed(self) -> int:
|
def mission_speed(self) -> Speed:
|
||||||
return GroundSpeed.for_flight(self.flight, self.ingress.alt)
|
return GroundSpeed.for_flight(self.flight, self.ingress.alt)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -865,8 +866,8 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
start, end = self.racetrack_for_objective(location)
|
start, end = self.racetrack_for_objective(location)
|
||||||
patrol_alt = meters(random.randint(
|
patrol_alt = meters(random.randint(
|
||||||
self.doctrine.min_patrol_altitude,
|
int(self.doctrine.min_patrol_altitude.meters),
|
||||||
self.doctrine.max_patrol_altitude
|
int(self.doctrine.max_patrol_altitude.meters)
|
||||||
))
|
))
|
||||||
|
|
||||||
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||||
@ -893,7 +894,7 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
heading = self._heading_to_package_airfield(target)
|
heading = self._heading_to_package_airfield(target)
|
||||||
start = target.point_from_heading(heading,
|
start = target.point_from_heading(heading,
|
||||||
-self.doctrine.sweep_distance)
|
-self.doctrine.sweep_distance.meters)
|
||||||
|
|
||||||
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||||
start, end = builder.sweep(start, target,
|
start, end = builder.sweep(start, target,
|
||||||
@ -930,10 +931,11 @@ class FlightPlanBuilder:
|
|||||||
closest_airfield.position
|
closest_airfield.position
|
||||||
)
|
)
|
||||||
|
|
||||||
min_distance_from_enemy = nm_to_meter(20)
|
min_distance_from_enemy = nautical_miles(20)
|
||||||
distance_to_airfield = int(closest_airfield.position.distance_to_point(
|
distance_to_airfield = meters(
|
||||||
self.package.target.position
|
closest_airfield.position.distance_to_point(
|
||||||
))
|
self.package.target.position
|
||||||
|
))
|
||||||
distance_to_no_fly = distance_to_airfield - min_distance_from_enemy
|
distance_to_no_fly = distance_to_airfield - min_distance_from_enemy
|
||||||
min_cap_distance = min(self.doctrine.cap_min_distance_from_cp,
|
min_cap_distance = min(self.doctrine.cap_min_distance_from_cp,
|
||||||
distance_to_no_fly)
|
distance_to_no_fly)
|
||||||
@ -942,11 +944,12 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
end = location.position.point_from_heading(
|
end = location.position.point_from_heading(
|
||||||
heading,
|
heading,
|
||||||
random.randint(min_cap_distance, max_cap_distance)
|
random.randint(int(min_cap_distance.meters),
|
||||||
|
int(max_cap_distance.meters))
|
||||||
)
|
)
|
||||||
diameter = random.randint(
|
diameter = random.randint(
|
||||||
self.doctrine.cap_min_track_length,
|
int(self.doctrine.cap_min_track_length.meters),
|
||||||
self.doctrine.cap_max_track_length
|
int(self.doctrine.cap_max_track_length.meters)
|
||||||
)
|
)
|
||||||
start = end.point_from_heading(heading - 180, diameter)
|
start = end.point_from_heading(heading - 180, diameter)
|
||||||
return start, end
|
return start, end
|
||||||
@ -961,7 +964,8 @@ class FlightPlanBuilder:
|
|||||||
)
|
)
|
||||||
center = ingress.point_from_heading(heading, distance / 2)
|
center = ingress.point_from_heading(heading, distance / 2)
|
||||||
orbit_center = center.point_from_heading(
|
orbit_center = center.point_from_heading(
|
||||||
heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15))
|
heading - 90, random.randint(int(nautical_miles(6).meters),
|
||||||
|
int(nautical_miles(15).meters))
|
||||||
)
|
)
|
||||||
|
|
||||||
combat_width = distance / 2
|
combat_width = distance / 2
|
||||||
@ -984,8 +988,9 @@ class FlightPlanBuilder:
|
|||||||
"""
|
"""
|
||||||
location = self.package.target
|
location = self.package.target
|
||||||
|
|
||||||
patrol_alt = meters(random.randint(self.doctrine.min_patrol_altitude,
|
patrol_alt = meters(
|
||||||
self.doctrine.max_patrol_altitude))
|
random.randint(int(self.doctrine.min_patrol_altitude.meters),
|
||||||
|
int(self.doctrine.max_patrol_altitude.meters)))
|
||||||
|
|
||||||
# Create points
|
# Create points
|
||||||
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||||
@ -1189,12 +1194,13 @@ class FlightPlanBuilder:
|
|||||||
# point, plan the hold point such that it retreats from the origin
|
# point, plan the hold point such that it retreats from the origin
|
||||||
# airfield.
|
# airfield.
|
||||||
return join.point_from_heading(target.heading_between_point(origin),
|
return join.point_from_heading(target.heading_between_point(origin),
|
||||||
self.doctrine.push_distance)
|
self.doctrine.push_distance.meters)
|
||||||
|
|
||||||
heading_to_join = origin.heading_between_point(join)
|
heading_to_join = origin.heading_between_point(join)
|
||||||
hold_point = origin.point_from_heading(heading_to_join,
|
hold_point = origin.point_from_heading(
|
||||||
self.doctrine.push_distance)
|
heading_to_join, self.doctrine.push_distance.meters)
|
||||||
if hold_point.distance_to_point(join) >= self.doctrine.push_distance:
|
hold_distance = meters(hold_point.distance_to_point(join))
|
||||||
|
if hold_distance >= self.doctrine.push_distance:
|
||||||
# Hold point is between the origin airfield and the join point and
|
# Hold point is between the origin airfield and the join point and
|
||||||
# spaced sufficiently.
|
# spaced sufficiently.
|
||||||
return hold_point
|
return hold_point
|
||||||
@ -1206,10 +1212,10 @@ class FlightPlanBuilder:
|
|||||||
# properly.
|
# properly.
|
||||||
origin_to_join = origin.distance_to_point(join)
|
origin_to_join = origin.distance_to_point(join)
|
||||||
cos_theta = (
|
cos_theta = (
|
||||||
(self.doctrine.hold_distance ** 2 +
|
(self.doctrine.hold_distance.meters ** 2 +
|
||||||
origin_to_join ** 2 -
|
origin_to_join ** 2 -
|
||||||
self.doctrine.join_distance ** 2) /
|
self.doctrine.join_distance.meters ** 2) /
|
||||||
(2 * self.doctrine.hold_distance * origin_to_join)
|
(2 * self.doctrine.hold_distance.meters * origin_to_join)
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
theta = math.acos(cos_theta)
|
theta = math.acos(cos_theta)
|
||||||
@ -1218,10 +1224,10 @@ class FlightPlanBuilder:
|
|||||||
# hold point away from the target.
|
# hold point away from the target.
|
||||||
return origin.point_from_heading(
|
return origin.point_from_heading(
|
||||||
target.heading_between_point(origin),
|
target.heading_between_point(origin),
|
||||||
self.doctrine.hold_distance)
|
self.doctrine.hold_distance.meters)
|
||||||
|
|
||||||
return origin.point_from_heading(heading_to_join - theta,
|
return origin.point_from_heading(heading_to_join - theta,
|
||||||
self.doctrine.hold_distance)
|
self.doctrine.hold_distance.meters)
|
||||||
|
|
||||||
# TODO: Make a model for the waypoint builder and use that in the UI.
|
# TODO: Make a model for the waypoint builder and use that in the UI.
|
||||||
def generate_rtb_waypoint(self, flight: Flight,
|
def generate_rtb_waypoint(self, flight: Flight,
|
||||||
@ -1273,13 +1279,13 @@ class FlightPlanBuilder:
|
|||||||
return attack_transition.point_from_heading(
|
return attack_transition.point_from_heading(
|
||||||
self.package.target.position.heading_between_point(
|
self.package.target.position.heading_between_point(
|
||||||
self.package_airfield().position),
|
self.package_airfield().position),
|
||||||
self.doctrine.join_distance)
|
self.doctrine.join_distance.meters)
|
||||||
|
|
||||||
def _advancing_rendezvous_point(self, attack_transition: Point) -> Point:
|
def _advancing_rendezvous_point(self, attack_transition: Point) -> Point:
|
||||||
"""Creates a rendezvous point that advances toward the target."""
|
"""Creates a rendezvous point that advances toward the target."""
|
||||||
heading = self._heading_to_package_airfield(attack_transition)
|
heading = self._heading_to_package_airfield(attack_transition)
|
||||||
return attack_transition.point_from_heading(
|
return attack_transition.point_from_heading(
|
||||||
heading, -self.doctrine.join_distance)
|
heading, -self.doctrine.join_distance.meters)
|
||||||
|
|
||||||
def _rendezvous_should_retreat(self, attack_transition: Point) -> bool:
|
def _rendezvous_should_retreat(self, attack_transition: Point) -> bool:
|
||||||
transition_target_distance = attack_transition.distance_to_point(
|
transition_target_distance = attack_transition.distance_to_point(
|
||||||
@ -1307,13 +1313,13 @@ class FlightPlanBuilder:
|
|||||||
def _ingress_point(self) -> Point:
|
def _ingress_point(self) -> Point:
|
||||||
heading = self._target_heading_to_package_airfield()
|
heading = self._target_heading_to_package_airfield()
|
||||||
return self.package.target.position.point_from_heading(
|
return self.package.target.position.point_from_heading(
|
||||||
heading - 180 + 25, self.doctrine.ingress_egress_distance
|
heading - 180 + 25, self.doctrine.ingress_egress_distance.meters
|
||||||
)
|
)
|
||||||
|
|
||||||
def _egress_point(self) -> Point:
|
def _egress_point(self) -> Point:
|
||||||
heading = self._target_heading_to_package_airfield()
|
heading = self._target_heading_to_package_airfield()
|
||||||
return self.package.target.position.point_from_heading(
|
return self.package.target.position.point_from_heading(
|
||||||
heading - 180 - 25, self.doctrine.ingress_egress_distance
|
heading - 180 - 25, self.doctrine.ingress_egress_distance.meters
|
||||||
)
|
)
|
||||||
|
|
||||||
def _target_heading_to_package_airfield(self) -> int:
|
def _target_heading_to_package_airfield(self) -> int:
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
from dcs.unittype import FlyingType
|
from dcs.unittype import FlyingType
|
||||||
@ -12,7 +12,8 @@ from game.utils import (
|
|||||||
Distance,
|
Distance,
|
||||||
SPEED_OF_SOUND_AT_SEA_LEVEL,
|
SPEED_OF_SOUND_AT_SEA_LEVEL,
|
||||||
Speed,
|
Speed,
|
||||||
kph, mach, meter_to_nm,
|
kph,
|
||||||
|
mach,
|
||||||
meters,
|
meters,
|
||||||
)
|
)
|
||||||
from gen.flights.flight import Flight
|
from gen.flights.flight import Flight
|
||||||
@ -24,7 +25,7 @@ if TYPE_CHECKING:
|
|||||||
class GroundSpeed:
|
class GroundSpeed:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def for_flight(cls, flight: Flight, altitude: Distance) -> int:
|
def for_flight(cls, flight: Flight, altitude: Distance) -> Speed:
|
||||||
if not issubclass(flight.unit_type, FlyingType):
|
if not issubclass(flight.unit_type, FlyingType):
|
||||||
raise TypeError("Flight has non-flying unit")
|
raise TypeError("Flight has non-flying unit")
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ class GroundSpeed:
|
|||||||
if max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL:
|
if max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL:
|
||||||
# Aircraft is supersonic. Limit to mach 0.8 to conserve fuel and
|
# Aircraft is supersonic. Limit to mach 0.8 to conserve fuel and
|
||||||
# account for heavily loaded jets.
|
# account for heavily loaded jets.
|
||||||
return int(mach(0.8, altitude).knots)
|
return mach(0.8, altitude)
|
||||||
|
|
||||||
# For subsonic aircraft, assume the aircraft can reasonably perform at
|
# For subsonic aircraft, assume the aircraft can reasonably perform at
|
||||||
# 80% of its maximum, and that it can maintain the same mach at altitude
|
# 80% of its maximum, and that it can maintain the same mach at altitude
|
||||||
@ -46,15 +47,16 @@ class GroundSpeed:
|
|||||||
# might. be sufficient given the wiggle room. We can come up with
|
# might. be sufficient given the wiggle room. We can come up with
|
||||||
# another heuristic if needed.
|
# another heuristic if needed.
|
||||||
cruise_mach = max_speed.mach() * 0.8
|
cruise_mach = max_speed.mach() * 0.8
|
||||||
return int(mach(cruise_mach, altitude).knots)
|
return mach(cruise_mach, altitude)
|
||||||
|
|
||||||
|
|
||||||
class TravelTime:
|
class TravelTime:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def between_points(a: Point, b: Point, speed: float) -> timedelta:
|
def between_points(a: Point, b: Point, speed: Speed) -> timedelta:
|
||||||
error_factor = 1.1
|
error_factor = 1.1
|
||||||
distance = meter_to_nm(a.distance_to_point(b))
|
distance = meters(a.distance_to_point(b))
|
||||||
return timedelta(hours=distance / speed * error_factor)
|
return timedelta(
|
||||||
|
hours=distance.nautical_miles / speed.knots * error_factor)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Most if not all of this should move into FlightPlan.
|
# TODO: Most if not all of this should move into FlightPlan.
|
||||||
|
|||||||
@ -34,7 +34,7 @@ from game.theater.theatergroundobject import (
|
|||||||
LhaGroundObject, ShipGroundObject,
|
LhaGroundObject, ShipGroundObject,
|
||||||
)
|
)
|
||||||
from game.unitmap import UnitMap
|
from game.unitmap import UnitMap
|
||||||
from game.utils import knots_to_kph, kph_to_mps, mps_to_kph
|
from game.utils import knots, mps
|
||||||
from .radios import RadioFrequency, RadioRegistry
|
from .radios import RadioFrequency, RadioRegistry
|
||||||
from .runways import RunwayData
|
from .runways import RunwayData
|
||||||
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
||||||
@ -247,13 +247,13 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator):
|
|||||||
wind = self.game.conditions.weather.wind.at_0m
|
wind = self.game.conditions.weather.wind.at_0m
|
||||||
brc = wind.direction + 180
|
brc = wind.direction + 180
|
||||||
# Aim for 25kts over the deck.
|
# Aim for 25kts over the deck.
|
||||||
carrier_speed = knots_to_kph(25) - mps_to_kph(wind.speed)
|
carrier_speed = knots(25) - mps(wind.speed)
|
||||||
for attempt in range(5):
|
for attempt in range(5):
|
||||||
point = group.points[0].position.point_from_heading(
|
point = group.points[0].position.point_from_heading(
|
||||||
brc, 100000 - attempt * 20000)
|
brc, 100000 - attempt * 20000)
|
||||||
if self.game.theater.is_in_sea(point):
|
if self.game.theater.is_in_sea(point):
|
||||||
group.points[0].speed = kph_to_mps(carrier_speed)
|
group.points[0].speed = carrier_speed.meters_per_second
|
||||||
group.add_waypoint(point, carrier_speed)
|
group.add_waypoint(point, carrier_speed.kph)
|
||||||
return brc
|
return brc
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@ -33,8 +33,7 @@ from dcs.mission import Mission
|
|||||||
from dcs.unittype import FlyingType
|
from dcs.unittype import FlyingType
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from game.utils import meter_to_nm
|
from game.utils import meters
|
||||||
from . import units
|
|
||||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||||
from .airsupportgen import AwacsInfo, TankerInfo
|
from .airsupportgen import AwacsInfo, TankerInfo
|
||||||
from .briefinggen import CommInfo, JtacInfo, MissionInfoGenerator
|
from .briefinggen import CommInfo, JtacInfo, MissionInfoGenerator
|
||||||
@ -170,10 +169,10 @@ class FlightPlanBuilder:
|
|||||||
if self.last_waypoint is None:
|
if self.last_waypoint is None:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
distance = meter_to_nm(self.last_waypoint.position.distance_to_point(
|
distance = meters(self.last_waypoint.position.distance_to_point(
|
||||||
waypoint.position
|
waypoint.position
|
||||||
))
|
))
|
||||||
return f"{distance} NM"
|
return f"{distance.nautical_miles:.1f} NM"
|
||||||
|
|
||||||
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:
|
||||||
@ -189,19 +188,11 @@ class FlightPlanBuilder:
|
|||||||
else:
|
else:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
distance = meter_to_nm(self.last_waypoint.position.distance_to_point(
|
distance = meters(self.last_waypoint.position.distance_to_point(
|
||||||
waypoint.position
|
waypoint.position
|
||||||
))
|
))
|
||||||
duration = (waypoint.tot - last_time).total_seconds() / 3600
|
duration = (waypoint.tot - last_time).total_seconds() / 3600
|
||||||
try:
|
return f"{int(distance.nautical_miles / duration)} kt"
|
||||||
return f"{int(distance / duration)} kt"
|
|
||||||
except ZeroDivisionError:
|
|
||||||
# TODO: Improve resolution of unit conversions.
|
|
||||||
# When waypoints are very close to each other they can end up with
|
|
||||||
# identical TOTs because our unit conversion functions truncate to
|
|
||||||
# int. When waypoints have the same TOT the duration will be zero.
|
|
||||||
# https://github.com/Khopa/dcs_liberation/issues/557
|
|
||||||
return "-"
|
|
||||||
|
|
||||||
def build(self) -> List[List[str]]:
|
def build(self) -> List[List[str]]:
|
||||||
return self.rows
|
return self.rows
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
from PySide2.QtCore import Qt
|
|
||||||
from PySide2.QtWidgets import QLabel, QHBoxLayout, QGroupBox, QVBoxLayout, QFrame, QGridLayout
|
|
||||||
from PySide2.QtGui import QPixmap
|
from PySide2.QtGui import QPixmap
|
||||||
|
from PySide2.QtWidgets import (
|
||||||
from game.weather import Conditions, TimeOfDay, Weather
|
QFrame,
|
||||||
from game.utils import meter_to_nm, mps_to_knots
|
QGridLayout,
|
||||||
|
QGroupBox,
|
||||||
|
QHBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
QVBoxLayout,
|
||||||
|
)
|
||||||
from dcs.weather import Weather as PydcsWeather
|
from dcs.weather import Weather as PydcsWeather
|
||||||
|
|
||||||
import qt_ui.uiconstants as CONST
|
import qt_ui.uiconstants as CONST
|
||||||
|
from game.utils import mps
|
||||||
|
from game.weather import Conditions, TimeOfDay
|
||||||
|
|
||||||
|
|
||||||
class QTimeTurnWidget(QGroupBox):
|
class QTimeTurnWidget(QGroupBox):
|
||||||
"""
|
"""
|
||||||
@ -163,20 +169,20 @@ class QWeatherWidget(QGroupBox):
|
|||||||
def updateWinds(self):
|
def updateWinds(self):
|
||||||
"""Updates the UI with the current conditions wind info.
|
"""Updates the UI with the current conditions wind info.
|
||||||
"""
|
"""
|
||||||
windGlSpeed = mps_to_knots(self.conditions.weather.wind.at_0m.speed or 0)
|
windGlSpeed = mps(self.conditions.weather.wind.at_0m.speed or 0)
|
||||||
windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, '0')
|
windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, '0')
|
||||||
self.windGLSpeedLabel.setText('{}kts'.format(windGlSpeed))
|
self.windGLSpeedLabel.setText(f'{int(windGlSpeed.knots)}kts')
|
||||||
self.windGLDirLabel.setText('{}º'.format(windGlDir))
|
self.windGLDirLabel.setText(f'{windGlDir}º')
|
||||||
|
|
||||||
windFL08Speed = mps_to_knots(self.conditions.weather.wind.at_2000m.speed or 0)
|
windFL08Speed = mps(self.conditions.weather.wind.at_2000m.speed or 0)
|
||||||
windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust(3, '0')
|
windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust(3, '0')
|
||||||
self.windFL08SpeedLabel.setText('{}kts'.format(windFL08Speed))
|
self.windFL08SpeedLabel.setText(f'{int(windFL08Speed.knots)}kts')
|
||||||
self.windFL08DirLabel.setText('{}º'.format(windFL08Dir))
|
self.windFL08DirLabel.setText(f'{windFL08Dir}º')
|
||||||
|
|
||||||
windFL26Speed = mps_to_knots(self.conditions.weather.wind.at_8000m.speed or 0)
|
windFL26Speed = mps(self.conditions.weather.wind.at_8000m.speed or 0)
|
||||||
windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust(3, '0')
|
windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust(3, '0')
|
||||||
self.windFL26SpeedLabel.setText('{}kts'.format(windFL26Speed))
|
self.windFL26SpeedLabel.setText(f'{int(windFL26Speed.knots)}kts')
|
||||||
self.windFL26DirLabel.setText('{}º'.format(windFL26Dir))
|
self.windFL26DirLabel.setText(f'{windFL26Dir}º')
|
||||||
|
|
||||||
def updateForecast(self):
|
def updateForecast(self):
|
||||||
"""Updates the Forecast Text and icon with the current conditions wind info.
|
"""Updates the Forecast Text and icon with the current conditions wind info.
|
||||||
@ -223,11 +229,10 @@ class QWeatherWidget(QGroupBox):
|
|||||||
if not fog:
|
if not fog:
|
||||||
self.forecastFog.setText('No fog')
|
self.forecastFog.setText('No fog')
|
||||||
else:
|
else:
|
||||||
visvibilityNm = round(meter_to_nm(fog.visibility), 1)
|
visibility = round(fog.visibility.nautical_miles, 1)
|
||||||
self.forecastFog.setText('Fog vis: {}nm'.format(visvibilityNm))
|
self.forecastFog.setText(f'Fog vis: {visibility}nm')
|
||||||
icon = [time, ('cloudy' if cloudDensity > 1 else None), 'fog']
|
icon = [time, ('cloudy' if cloudDensity > 1 else None), 'fog']
|
||||||
|
|
||||||
|
|
||||||
icon_key = "Weather_{}".format('-'.join(filter(None.__ne__, icon)))
|
icon_key = "Weather_{}".format('-'.join(filter(None.__ne__, icon)))
|
||||||
icon = CONST.ICONS.get(icon_key) or CONST.ICONS['Weather_night-partly-cloudy']
|
icon = CONST.ICONS.get(icon_key) or CONST.ICONS['Weather_night-partly-cloudy']
|
||||||
self.weather_icon.setPixmap(icon)
|
self.weather_icon.setPixmap(icon)
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import logging
|
|||||||
import math
|
import math
|
||||||
from typing import Iterable, Iterator, List, Optional, Tuple
|
from typing import Iterable, Iterator, List, Optional, Tuple
|
||||||
|
|
||||||
from PySide2 import QtWidgets, QtCore
|
from PySide2 import QtCore, QtWidgets
|
||||||
from PySide2.QtCore import QPointF, Qt, QLineF, QRectF
|
from PySide2.QtCore import QLineF, QPointF, QRectF, Qt
|
||||||
from PySide2.QtGui import (
|
from PySide2.QtGui import (
|
||||||
QBrush,
|
QBrush,
|
||||||
QColor,
|
QColor,
|
||||||
@ -14,13 +14,15 @@ from PySide2.QtGui import (
|
|||||||
QPen,
|
QPen,
|
||||||
QPixmap,
|
QPixmap,
|
||||||
QPolygonF,
|
QPolygonF,
|
||||||
QWheelEvent, )
|
QWheelEvent,
|
||||||
|
)
|
||||||
from PySide2.QtWidgets import (
|
from PySide2.QtWidgets import (
|
||||||
QFrame,
|
QFrame,
|
||||||
QGraphicsItem,
|
QGraphicsItem,
|
||||||
QGraphicsOpacityEffect,
|
QGraphicsOpacityEffect,
|
||||||
QGraphicsScene,
|
QGraphicsScene,
|
||||||
QGraphicsView, QGraphicsSceneMouseEvent,
|
QGraphicsSceneMouseEvent,
|
||||||
|
QGraphicsView,
|
||||||
)
|
)
|
||||||
from dcs import Point
|
from dcs import Point
|
||||||
from dcs.mapping import point_from_heading
|
from dcs.mapping import point_from_heading
|
||||||
@ -32,7 +34,7 @@ from game.theater.conflicttheater import FrontLine, ReferencePoint
|
|||||||
from game.theater.theatergroundobject import (
|
from game.theater.theatergroundobject import (
|
||||||
TheaterGroundObject,
|
TheaterGroundObject,
|
||||||
)
|
)
|
||||||
from game.utils import meter_to_feet, nm_to_meter, meter_to_nm
|
from game.utils import Distance, meters, nautical_miles
|
||||||
from game.weather import TimeOfDay
|
from game.weather import TimeOfDay
|
||||||
from gen import Conflict
|
from gen import Conflict
|
||||||
from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType
|
from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType
|
||||||
@ -45,7 +47,7 @@ from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint
|
|||||||
from qt_ui.widgets.map.QMapGroundObject import QMapGroundObject
|
from qt_ui.widgets.map.QMapGroundObject import QMapGroundObject
|
||||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||||
|
|
||||||
MAX_SHIP_DISTANCE = 80
|
MAX_SHIP_DISTANCE = nautical_miles(80)
|
||||||
|
|
||||||
def binomial(i: int, n: int) -> float:
|
def binomial(i: int, n: int) -> float:
|
||||||
"""Binomial coefficient"""
|
"""Binomial coefficient"""
|
||||||
@ -171,7 +173,7 @@ class QLiberationMap(QGraphicsView):
|
|||||||
self.game = game
|
self.game = game
|
||||||
if self.game is not None:
|
if self.game is not None:
|
||||||
logging.debug("Reloading Map Canvas")
|
logging.debug("Reloading Map Canvas")
|
||||||
self.nm_to_pixel_ratio = self.km_to_pixel(float(nm_to_meter(1)) / 1000.0)
|
self.nm_to_pixel_ratio = self.distance_to_pixels(nautical_miles(1))
|
||||||
self.reload_scene()
|
self.reload_scene()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -567,7 +569,7 @@ class QLiberationMap(QGraphicsView):
|
|||||||
BIG_LINE = 5
|
BIG_LINE = 5
|
||||||
SMALL_LINE = 2
|
SMALL_LINE = 2
|
||||||
|
|
||||||
dist = self.km_to_pixel(nm_to_meter(scale_distance_nm)/1000.0)
|
dist = self.distance_to_pixels(nautical_miles(scale_distance_nm))
|
||||||
self.scene().addRect(POS_X, POS_Y-PADDING, PADDING*2 + dist, BIG_LINE*2+3*PADDING, pen=CONST.COLORS["black"], brush=CONST.COLORS["black"])
|
self.scene().addRect(POS_X, POS_Y-PADDING, PADDING*2 + dist, BIG_LINE*2+3*PADDING, pen=CONST.COLORS["black"], brush=CONST.COLORS["black"])
|
||||||
l = self.scene().addLine(POS_X + PADDING, POS_Y + BIG_LINE*2, POS_X + PADDING + dist, POS_Y + BIG_LINE*2)
|
l = self.scene().addLine(POS_X + PADDING, POS_Y + BIG_LINE*2, POS_X + PADDING + dist, POS_Y + BIG_LINE*2)
|
||||||
|
|
||||||
@ -663,12 +665,12 @@ class QLiberationMap(QGraphicsView):
|
|||||||
Point(offset.x / scale.x, offset.y / scale.y))
|
Point(offset.x / scale.x, offset.y / scale.y))
|
||||||
return point_a.world_coordinates - scaled
|
return point_a.world_coordinates - scaled
|
||||||
|
|
||||||
def km_to_pixel(self, km):
|
def distance_to_pixels(self, distance: Distance) -> int:
|
||||||
p1 = Point(0, 0)
|
p1 = Point(0, 0)
|
||||||
p2 = Point(0, 1000*km)
|
p2 = Point(0, distance.meters)
|
||||||
p1a = Point(*self._transform_point(p1))
|
p1a = Point(*self._transform_point(p1))
|
||||||
p2a = Point(*self._transform_point(p2))
|
p2a = Point(*self._transform_point(p2))
|
||||||
return p1a.distance_to_point(p2a)
|
return int(p1a.distance_to_point(p2a))
|
||||||
|
|
||||||
def highlight_color(self, transparent: Optional[bool] = False) -> QColor:
|
def highlight_color(self, transparent: Optional[bool] = False) -> QColor:
|
||||||
return QColor(255, 255, 0, 20 if transparent else 255)
|
return QColor(255, 255, 0, 20 if transparent else 255)
|
||||||
@ -820,7 +822,7 @@ class QLiberationMap(QGraphicsView):
|
|||||||
distance = self.selected_cp.control_point.position.distance_to_point(
|
distance = self.selected_cp.control_point.position.distance_to_point(
|
||||||
world_destination
|
world_destination
|
||||||
)
|
)
|
||||||
if meter_to_nm(distance) > MAX_SHIP_DISTANCE:
|
if meters(distance) > MAX_SHIP_DISTANCE:
|
||||||
return False
|
return False
|
||||||
return self.game.theater.is_in_sea(world_destination)
|
return self.game.theater.is_in_sea(world_destination)
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from PySide2.QtCore import QItemSelectionModel, QPoint
|
|||||||
from PySide2.QtGui import QStandardItem, QStandardItemModel
|
from PySide2.QtGui import QStandardItem, QStandardItemModel
|
||||||
from PySide2.QtWidgets import QHeaderView, QTableView
|
from PySide2.QtWidgets import QHeaderView, QTableView
|
||||||
|
|
||||||
from game.utils import meter_to_feet
|
|
||||||
from gen.ato import Package
|
from gen.ato import Package
|
||||||
from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType
|
from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType
|
||||||
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import \
|
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import \
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user