mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Un-pydantic FlightWaypoint.
Apparently it's a bad idea to try to make the core data pydantic models, and those should really be treated more as a view-model. Doing otherwise causes odd patterns (like the UI info I had leaked into the core type), and makes it harder to interop with third-party types.
This commit is contained in:
parent
c5ab0431a9
commit
3e08e0e8b6
@ -1,93 +1,31 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from dataclasses import field
|
from dataclasses import dataclass, field
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Literal, TYPE_CHECKING
|
||||||
|
|
||||||
from dcs import Point
|
from dcs import Point
|
||||||
from dcs.unit import Unit
|
|
||||||
from pydantic.dataclasses import dataclass
|
|
||||||
|
|
||||||
from game.ato.flightwaypointtype import FlightWaypointType
|
from game.ato.flightwaypointtype import FlightWaypointType
|
||||||
from game.theater import LatLon
|
|
||||||
from game.theater.theatergroup import TheaterUnit
|
from game.theater.theatergroup import TheaterUnit
|
||||||
from game.utils import Distance, meters
|
from game.utils import Distance, meters
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.theater import ConflictTheater, ControlPoint, MissionTarget
|
from game.theater import ControlPoint, MissionTarget
|
||||||
|
|
||||||
|
|
||||||
|
AltitudeReference = Literal["BARO", "RADIO"]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BaseFlightWaypoint:
|
class FlightWaypoint:
|
||||||
name: str
|
name: str
|
||||||
waypoint_type: FlightWaypointType
|
waypoint_type: FlightWaypointType
|
||||||
x: float
|
x: float
|
||||||
y: float
|
y: float
|
||||||
alt: Distance
|
alt: Distance = meters(0)
|
||||||
alt_type: str
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
|
||||||
is_movable: bool = field(init=False)
|
|
||||||
should_mark: bool = field(init=False)
|
|
||||||
include_in_path: bool = field(init=False)
|
|
||||||
|
|
||||||
# Do not use unless you're sure it's up to date. Pydantic doesn't have support for
|
|
||||||
# serializing lazy properties so this needs to be stored in the class, but because
|
|
||||||
# updating it requires a reference to the ConflictTheater it may not always be set,
|
|
||||||
# or up to date. Call update_latlng to refresh.
|
|
||||||
latlng: LatLon | None = None
|
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
|
||||||
# Target *points* are the exact location of a unit, whereas the target area is
|
|
||||||
# only the center of the objective. Allow moving the latter since its exact
|
|
||||||
# location isn't very important.
|
|
||||||
#
|
|
||||||
# Landing, and divert should be changed in the flight settings UI, takeoff
|
|
||||||
# cannot be changed because that's where the plane is.
|
|
||||||
#
|
|
||||||
# Moving the bullseye reference only makes it wrong.
|
|
||||||
self.is_movable = self.waypoint_type not in {
|
|
||||||
FlightWaypointType.BULLSEYE,
|
|
||||||
FlightWaypointType.DIVERT,
|
|
||||||
FlightWaypointType.LANDING_POINT,
|
|
||||||
FlightWaypointType.TAKEOFF,
|
|
||||||
FlightWaypointType.TARGET_POINT,
|
|
||||||
}
|
|
||||||
|
|
||||||
# We don't need a marker for the departure waypoint (and it's likely
|
|
||||||
# coincident with the landing waypoint, so hard to see). We do want to draw
|
|
||||||
# the path from it though.
|
|
||||||
#
|
|
||||||
# We also don't need the landing waypoint since we'll be drawing that path
|
|
||||||
# as well, and it's clear what it is, and only obscured the CP icon.
|
|
||||||
#
|
|
||||||
# The divert waypoint also obscures the CP. We don't draw the path to it,
|
|
||||||
# but it can be seen in the flight settings page, so it's not really a
|
|
||||||
# problem to exclude it.
|
|
||||||
#
|
|
||||||
# Bullseye ought to be (but currently isn't) drawn *once* rather than as a
|
|
||||||
# flight waypoint.
|
|
||||||
self.should_mark = self.waypoint_type not in {
|
|
||||||
FlightWaypointType.BULLSEYE,
|
|
||||||
FlightWaypointType.DIVERT,
|
|
||||||
FlightWaypointType.LANDING_POINT,
|
|
||||||
FlightWaypointType.TAKEOFF,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.include_in_path = self.waypoint_type not in {
|
|
||||||
FlightWaypointType.BULLSEYE,
|
|
||||||
FlightWaypointType.DIVERT,
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def position(self) -> Point:
|
|
||||||
return Point(self.x, self.y)
|
|
||||||
|
|
||||||
def update_latlng(self, theater: ConflictTheater) -> None:
|
|
||||||
self.latlng = theater.point_to_ll(self.position)
|
|
||||||
|
|
||||||
|
|
||||||
class FlightWaypoint(BaseFlightWaypoint):
|
|
||||||
control_point: ControlPoint | None = None
|
control_point: ControlPoint | None = None
|
||||||
|
|
||||||
# TODO: Merge with pretty_name.
|
# TODO: Merge with pretty_name.
|
||||||
@ -95,7 +33,7 @@ class FlightWaypoint(BaseFlightWaypoint):
|
|||||||
# having three names. A short and long form is enough.
|
# having three names. A short and long form is enough.
|
||||||
description: str = ""
|
description: str = ""
|
||||||
|
|
||||||
targets: Sequence[MissionTarget | TheaterUnit] = []
|
targets: Sequence[MissionTarget | TheaterUnit] = field(default_factory=list)
|
||||||
obj_name: str = ""
|
obj_name: str = ""
|
||||||
pretty_name: str = ""
|
pretty_name: str = ""
|
||||||
only_for_player: bool = False
|
only_for_player: bool = False
|
||||||
@ -111,29 +49,9 @@ class FlightWaypoint(BaseFlightWaypoint):
|
|||||||
tot: timedelta | None = None
|
tot: timedelta | None = None
|
||||||
departure_time: timedelta | None = None
|
departure_time: timedelta | None = None
|
||||||
|
|
||||||
def __init__(
|
@property
|
||||||
self,
|
def position(self) -> Point:
|
||||||
waypoint_type: FlightWaypointType,
|
return Point(self.x, self.y)
|
||||||
x: float,
|
|
||||||
y: float,
|
|
||||||
alt: Distance = meters(0),
|
|
||||||
control_point: Optional[ControlPoint] = None,
|
|
||||||
) -> None:
|
|
||||||
"""Creates a flight waypoint.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
waypoint_type: The waypoint type.
|
|
||||||
x: X coordinate of the waypoint.
|
|
||||||
y: Y coordinate of the waypoint.
|
|
||||||
alt: Altitude of the waypoint. By default this is MSL, but it can be
|
|
||||||
changed to AGL by setting alt_type to "RADIO"
|
|
||||||
control_point: The control point to associate with this waypoint. Needed for
|
|
||||||
landing points.
|
|
||||||
"""
|
|
||||||
super().__init__(
|
|
||||||
name="", waypoint_type=waypoint_type, x=x, y=y, alt=alt, alt_type="BARO"
|
|
||||||
)
|
|
||||||
self.control_point = control_point
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(id(self))
|
return hash(id(self))
|
||||||
|
|||||||
73
game/server/waypoints/models.py
Normal file
73
game/server/waypoints/models.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
|
from game.ato import FlightWaypoint
|
||||||
|
from game.ato.flightwaypointtype import FlightWaypointType
|
||||||
|
from game.theater import ConflictTheater, LatLon
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FlightWaypointJs:
|
||||||
|
name: str
|
||||||
|
position: LatLon
|
||||||
|
altitude_ft: float
|
||||||
|
altitude_reference: str
|
||||||
|
is_movable: bool
|
||||||
|
should_mark: bool
|
||||||
|
include_in_path: bool
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def for_waypoint(
|
||||||
|
waypoint: FlightWaypoint, theater: ConflictTheater
|
||||||
|
) -> FlightWaypointJs:
|
||||||
|
# Target *points* are the exact location of a unit, whereas the target area is
|
||||||
|
# only the center of the objective. Allow moving the latter since its exact
|
||||||
|
# location isn't very important.
|
||||||
|
#
|
||||||
|
# Landing, and divert should be changed in the flight settings UI, takeoff
|
||||||
|
# cannot be changed because that's where the plane is.
|
||||||
|
#
|
||||||
|
# Moving the bullseye reference only makes it wrong.
|
||||||
|
is_movable = waypoint.waypoint_type not in {
|
||||||
|
FlightWaypointType.BULLSEYE,
|
||||||
|
FlightWaypointType.DIVERT,
|
||||||
|
FlightWaypointType.LANDING_POINT,
|
||||||
|
FlightWaypointType.TAKEOFF,
|
||||||
|
FlightWaypointType.TARGET_POINT,
|
||||||
|
}
|
||||||
|
|
||||||
|
# We don't need a marker for the departure waypoint (and it's likely
|
||||||
|
# coincident with the landing waypoint, so hard to see). We do want to draw
|
||||||
|
# the path from it though.
|
||||||
|
#
|
||||||
|
# We also don't need the landing waypoint since we'll be drawing that path
|
||||||
|
# as well, and it's clear what it is, and only obscured the CP icon.
|
||||||
|
#
|
||||||
|
# The divert waypoint also obscures the CP. We don't draw the path to it,
|
||||||
|
# but it can be seen in the flight settings page, so it's not really a
|
||||||
|
# problem to exclude it.
|
||||||
|
#
|
||||||
|
# Bullseye ought to be (but currently isn't) drawn *once* rather than as a
|
||||||
|
# flight waypoint.
|
||||||
|
should_mark = waypoint.waypoint_type not in {
|
||||||
|
FlightWaypointType.BULLSEYE,
|
||||||
|
FlightWaypointType.DIVERT,
|
||||||
|
FlightWaypointType.LANDING_POINT,
|
||||||
|
FlightWaypointType.TAKEOFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
include_in_path = waypoint.waypoint_type not in {
|
||||||
|
FlightWaypointType.BULLSEYE,
|
||||||
|
FlightWaypointType.DIVERT,
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlightWaypointJs(
|
||||||
|
name=waypoint.name,
|
||||||
|
position=theater.point_to_ll(waypoint.position),
|
||||||
|
altitude_ft=waypoint.alt.feet,
|
||||||
|
altitude_reference=waypoint.alt_type,
|
||||||
|
is_movable=is_movable,
|
||||||
|
should_mark=should_mark,
|
||||||
|
include_in_path=include_in_path,
|
||||||
|
)
|
||||||
@ -4,31 +4,36 @@ from uuid import UUID
|
|||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
|
|
||||||
from game import Game
|
from game import Game
|
||||||
from game.ato.flightwaypoint import BaseFlightWaypoint, FlightWaypoint
|
from game.ato.flightwaypoint import FlightWaypoint
|
||||||
from game.ato.flightwaypointtype import FlightWaypointType
|
from game.ato.flightwaypointtype import FlightWaypointType
|
||||||
from game.server import GameContext
|
from game.server import GameContext
|
||||||
|
from game.server.waypoints.models import FlightWaypointJs
|
||||||
from game.theater import LatLon
|
from game.theater import LatLon
|
||||||
from game.utils import meters
|
from game.utils import meters
|
||||||
|
|
||||||
router: APIRouter = APIRouter(prefix="/waypoints")
|
router: APIRouter = APIRouter(prefix="/waypoints")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{flight_id}", response_model=list[BaseFlightWaypoint])
|
@router.get("/{flight_id}", response_model=list[FlightWaypointJs])
|
||||||
def all_waypoints_for_flight(
|
def all_waypoints_for_flight(
|
||||||
flight_id: UUID, game: Game = Depends(GameContext.get)
|
flight_id: UUID, game: Game = Depends(GameContext.get)
|
||||||
) -> list[FlightWaypoint]:
|
) -> list[FlightWaypointJs]:
|
||||||
flight = game.db.flights.get(flight_id)
|
flight = game.db.flights.get(flight_id)
|
||||||
departure = FlightWaypoint(
|
departure = FlightWaypointJs.for_waypoint(
|
||||||
FlightWaypointType.TAKEOFF,
|
FlightWaypoint(
|
||||||
flight.departure.position.x,
|
"TAKEOFF",
|
||||||
flight.departure.position.y,
|
FlightWaypointType.TAKEOFF,
|
||||||
meters(0),
|
flight.departure.position.x,
|
||||||
|
flight.departure.position.y,
|
||||||
|
meters(0),
|
||||||
|
"RADIO",
|
||||||
|
),
|
||||||
|
game.theater,
|
||||||
)
|
)
|
||||||
departure.alt_type = "RADIO"
|
return [departure] + [
|
||||||
points = [departure] + flight.flight_plan.waypoints
|
FlightWaypointJs.for_waypoint(w, game.theater)
|
||||||
for point in points:
|
for w in flight.flight_plan.waypoints
|
||||||
point.update_latlng(game.theater)
|
]
|
||||||
return points
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{flight_id}/{waypoint_idx}/position")
|
@router.post("/{flight_id}/{waypoint_idx}/position")
|
||||||
@ -43,9 +48,6 @@ def set_position(
|
|||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
waypoint = flight.flight_plan.waypoints[waypoint_idx - 1]
|
waypoint = flight.flight_plan.waypoints[waypoint_idx - 1]
|
||||||
if not waypoint.is_movable:
|
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
|
||||||
|
|
||||||
point = game.theater.ll_to_point(position)
|
point = game.theater.ll_to_point(position)
|
||||||
waypoint.x = point.x
|
waypoint.x = point.x
|
||||||
waypoint.y = point.y
|
waypoint.y = point.y
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import math
|
|||||||
import random
|
import random
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import TypeVar, Union
|
from typing import TypeVar, Union
|
||||||
|
|
||||||
from dcs import Point
|
from dcs import Point
|
||||||
from pydantic.dataclasses import dataclass
|
|
||||||
from shapely.geometry import Point as ShapelyPoint
|
from shapely.geometry import Point as ShapelyPoint
|
||||||
|
|
||||||
METERS_TO_FEET = 3.28084
|
METERS_TO_FEET = 3.28084
|
||||||
|
|||||||
@ -24,7 +24,6 @@ from typing import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
from dcs.unit import Unit
|
|
||||||
from shapely.geometry import Point as ShapelyPoint
|
from shapely.geometry import Point as ShapelyPoint
|
||||||
|
|
||||||
from game.ato.flighttype import FlightType
|
from game.ato.flighttype import FlightType
|
||||||
@ -700,10 +699,12 @@ class StrikeFlightPlan(FormationFlightPlan):
|
|||||||
@property
|
@property
|
||||||
def target_area_waypoint(self) -> FlightWaypoint:
|
def target_area_waypoint(self) -> FlightWaypoint:
|
||||||
return FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"TARGET AREA",
|
||||||
FlightWaypointType.TARGET_GROUP_LOC,
|
FlightWaypointType.TARGET_GROUP_LOC,
|
||||||
self.package.target.position.x,
|
self.package.target.position.x,
|
||||||
self.package.target.position.y,
|
self.package.target.position.y,
|
||||||
meters(0),
|
meters(0),
|
||||||
|
"RADIO",
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -903,10 +904,12 @@ class RefuelingFlightPlan(PatrollingFlightPlan):
|
|||||||
class PackageRefuelingFlightPlan(RefuelingFlightPlan):
|
class PackageRefuelingFlightPlan(RefuelingFlightPlan):
|
||||||
def target_area_waypoint(self) -> FlightWaypoint:
|
def target_area_waypoint(self) -> FlightWaypoint:
|
||||||
return FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"TARGET AREA",
|
||||||
FlightWaypointType.TARGET_GROUP_LOC,
|
FlightWaypointType.TARGET_GROUP_LOC,
|
||||||
self.package.target.position.x,
|
self.package.target.position.x,
|
||||||
self.package.target.position.y,
|
self.package.target.position.y,
|
||||||
meters(0),
|
meters(0),
|
||||||
|
"RADIO",
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -921,13 +924,13 @@ class PackageRefuelingFlightPlan(RefuelingFlightPlan):
|
|||||||
# Cheat in a FlightWaypoint for the split point.
|
# Cheat in a FlightWaypoint for the split point.
|
||||||
split: Point = self.package.waypoints.split
|
split: Point = self.package.waypoints.split
|
||||||
split_waypoint: FlightWaypoint = FlightWaypoint(
|
split_waypoint: FlightWaypoint = FlightWaypoint(
|
||||||
FlightWaypointType.SPLIT, split.x, split.y, altitude
|
"SPLIT", FlightWaypointType.SPLIT, split.x, split.y, altitude
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cheat in a FlightWaypoint for the refuel point.
|
# Cheat in a FlightWaypoint for the refuel point.
|
||||||
refuel: Point = self.package.waypoints.refuel
|
refuel: Point = self.package.waypoints.refuel
|
||||||
refuel_waypoint: FlightWaypoint = FlightWaypoint(
|
refuel_waypoint: FlightWaypoint = FlightWaypoint(
|
||||||
FlightWaypointType.REFUEL, refuel.x, refuel.y, altitude
|
"REFUEL", FlightWaypointType.REFUEL, refuel.x, refuel.y, altitude
|
||||||
)
|
)
|
||||||
|
|
||||||
delay_target_to_split: timedelta = self.travel_time_between_waypoints(
|
delay_target_to_split: timedelta = self.travel_time_between_waypoints(
|
||||||
|
|||||||
@ -14,6 +14,8 @@ from typing import (
|
|||||||
|
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
|
|
||||||
|
from game.ato.flightwaypoint import AltitudeReference, FlightWaypoint
|
||||||
|
from game.ato.flightwaypointtype import FlightWaypointType
|
||||||
from game.theater import (
|
from game.theater import (
|
||||||
ControlPoint,
|
ControlPoint,
|
||||||
MissionTarget,
|
MissionTarget,
|
||||||
@ -22,8 +24,6 @@ from game.theater import (
|
|||||||
TheaterUnit,
|
TheaterUnit,
|
||||||
)
|
)
|
||||||
from game.utils import Distance, meters, nautical_miles
|
from game.utils import Distance, meters, nautical_miles
|
||||||
from game.ato.flightwaypointtype import FlightWaypointType
|
|
||||||
from game.ato.flightwaypoint import FlightWaypoint
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.ato.flight import Flight
|
from game.ato.flight import Flight
|
||||||
@ -68,25 +68,26 @@ class WaypointBuilder:
|
|||||||
"""
|
"""
|
||||||
position = departure.position
|
position = departure.position
|
||||||
if isinstance(departure, OffMapSpawn):
|
if isinstance(departure, OffMapSpawn):
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"NAV",
|
||||||
FlightWaypointType.NAV,
|
FlightWaypointType.NAV,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
||||||
|
description="Enter theater",
|
||||||
|
pretty_name="Enter theater",
|
||||||
)
|
)
|
||||||
waypoint.name = "NAV"
|
|
||||||
waypoint.alt_type = "BARO"
|
return FlightWaypoint(
|
||||||
waypoint.description = "Enter theater"
|
"TAKEOFF",
|
||||||
waypoint.pretty_name = "Enter theater"
|
FlightWaypointType.TAKEOFF,
|
||||||
else:
|
position.x,
|
||||||
waypoint = FlightWaypoint(
|
position.y,
|
||||||
FlightWaypointType.TAKEOFF, position.x, position.y, meters(0)
|
meters(0),
|
||||||
)
|
alt_type="RADIO",
|
||||||
waypoint.name = "TAKEOFF"
|
description="Takeoff",
|
||||||
waypoint.alt_type = "RADIO"
|
pretty_name="Takeoff",
|
||||||
waypoint.description = "Takeoff"
|
)
|
||||||
waypoint.pretty_name = "Takeoff"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def land(self, arrival: ControlPoint) -> FlightWaypoint:
|
def land(self, arrival: ControlPoint) -> FlightWaypoint:
|
||||||
"""Create descent waypoint for the given arrival airfield or carrier.
|
"""Create descent waypoint for the given arrival airfield or carrier.
|
||||||
@ -96,29 +97,27 @@ class WaypointBuilder:
|
|||||||
"""
|
"""
|
||||||
position = arrival.position
|
position = arrival.position
|
||||||
if isinstance(arrival, OffMapSpawn):
|
if isinstance(arrival, OffMapSpawn):
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"NAV",
|
||||||
FlightWaypointType.NAV,
|
FlightWaypointType.NAV,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
||||||
|
description="Exit theater",
|
||||||
|
pretty_name="Exit theater",
|
||||||
)
|
)
|
||||||
waypoint.name = "NAV"
|
|
||||||
waypoint.alt_type = "BARO"
|
return FlightWaypoint(
|
||||||
waypoint.description = "Exit theater"
|
"LANDING",
|
||||||
waypoint.pretty_name = "Exit theater"
|
FlightWaypointType.LANDING_POINT,
|
||||||
else:
|
position.x,
|
||||||
waypoint = FlightWaypoint(
|
position.y,
|
||||||
FlightWaypointType.LANDING_POINT,
|
meters(0),
|
||||||
position.x,
|
alt_type="RADIO",
|
||||||
position.y,
|
description="Land",
|
||||||
meters(0),
|
pretty_name="Land",
|
||||||
control_point=arrival,
|
control_point=arrival,
|
||||||
)
|
)
|
||||||
waypoint.name = "LANDING"
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.description = "Land"
|
|
||||||
waypoint.pretty_name = "Land"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def divert(self, divert: Optional[ControlPoint]) -> Optional[FlightWaypoint]:
|
def divert(self, divert: Optional[ControlPoint]) -> Optional[FlightWaypoint]:
|
||||||
"""Create divert waypoint for the given arrival airfield or carrier.
|
"""Create divert waypoint for the given arrival airfield or carrier.
|
||||||
@ -130,6 +129,7 @@ class WaypointBuilder:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
position = divert.position
|
position = divert.position
|
||||||
|
altitude_type: AltitudeReference
|
||||||
if isinstance(divert, OffMapSpawn):
|
if isinstance(divert, OffMapSpawn):
|
||||||
if self.is_helo:
|
if self.is_helo:
|
||||||
altitude = meters(500)
|
altitude = meters(500)
|
||||||
@ -140,88 +140,94 @@ class WaypointBuilder:
|
|||||||
altitude = meters(0)
|
altitude = meters(0)
|
||||||
altitude_type = "RADIO"
|
altitude_type = "RADIO"
|
||||||
|
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"DIVERT",
|
||||||
FlightWaypointType.DIVERT,
|
FlightWaypointType.DIVERT,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
altitude,
|
altitude,
|
||||||
|
alt_type=altitude_type,
|
||||||
|
description="Divert",
|
||||||
|
pretty_name="Divert",
|
||||||
|
only_for_player=True,
|
||||||
control_point=divert,
|
control_point=divert,
|
||||||
)
|
)
|
||||||
waypoint.alt_type = altitude_type
|
|
||||||
waypoint.name = "DIVERT"
|
|
||||||
waypoint.description = "Divert"
|
|
||||||
waypoint.pretty_name = "Divert"
|
|
||||||
waypoint.only_for_player = True
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def bullseye(self) -> FlightWaypoint:
|
def bullseye(self) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"BULLSEYE",
|
||||||
FlightWaypointType.BULLSEYE,
|
FlightWaypointType.BULLSEYE,
|
||||||
self._bullseye.position.x,
|
self._bullseye.position.x,
|
||||||
self._bullseye.position.y,
|
self._bullseye.position.y,
|
||||||
meters(0),
|
meters(0),
|
||||||
|
description="Bullseye",
|
||||||
|
pretty_name="Bullseye",
|
||||||
|
only_for_player=True,
|
||||||
)
|
)
|
||||||
waypoint.pretty_name = "Bullseye"
|
|
||||||
waypoint.description = "Bullseye"
|
|
||||||
waypoint.name = "BULLSEYE"
|
|
||||||
waypoint.only_for_player = True
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def hold(self, position: Point) -> FlightWaypoint:
|
def hold(self, position: Point) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
if self.is_helo:
|
||||||
|
alt_type = "RADIO"
|
||||||
|
|
||||||
|
return FlightWaypoint(
|
||||||
|
"HOLD",
|
||||||
FlightWaypointType.LOITER,
|
FlightWaypointType.LOITER,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
||||||
|
alt_type,
|
||||||
|
description="Wait until push time",
|
||||||
|
pretty_name="Hold",
|
||||||
)
|
)
|
||||||
if self.is_helo:
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.pretty_name = "Hold"
|
|
||||||
waypoint.description = "Wait until push time"
|
|
||||||
waypoint.name = "HOLD"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def join(self, position: Point) -> FlightWaypoint:
|
def join(self, position: Point) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
if self.is_helo:
|
||||||
|
alt_type = "RADIO"
|
||||||
|
|
||||||
|
return FlightWaypoint(
|
||||||
|
"JOIN",
|
||||||
FlightWaypointType.JOIN,
|
FlightWaypointType.JOIN,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
||||||
|
alt_type,
|
||||||
|
description="Rendezvous with package",
|
||||||
|
pretty_name="Join",
|
||||||
)
|
)
|
||||||
if self.is_helo:
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.pretty_name = "Join"
|
|
||||||
waypoint.description = "Rendezvous with package"
|
|
||||||
waypoint.name = "JOIN"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def refuel(self, position: Point) -> FlightWaypoint:
|
def refuel(self, position: Point) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
if self.is_helo:
|
||||||
|
alt_type = "RADIO"
|
||||||
|
|
||||||
|
return FlightWaypoint(
|
||||||
|
"REFUEL",
|
||||||
FlightWaypointType.REFUEL,
|
FlightWaypointType.REFUEL,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
||||||
|
alt_type,
|
||||||
|
description="Refuel from tanker",
|
||||||
|
pretty_name="Refuel",
|
||||||
)
|
)
|
||||||
if self.is_helo:
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.pretty_name = "Refuel"
|
|
||||||
waypoint.description = "Refuel from tanker"
|
|
||||||
waypoint.name = "REFUEL"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def split(self, position: Point) -> FlightWaypoint:
|
def split(self, position: Point) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
if self.is_helo:
|
||||||
|
alt_type = "RADIO"
|
||||||
|
|
||||||
|
return FlightWaypoint(
|
||||||
|
"SPLIT",
|
||||||
FlightWaypointType.SPLIT,
|
FlightWaypointType.SPLIT,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
||||||
|
alt_type,
|
||||||
|
description="Depart from package",
|
||||||
|
pretty_name="Split",
|
||||||
)
|
)
|
||||||
if self.is_helo:
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.pretty_name = "Split"
|
|
||||||
waypoint.description = "Depart from package"
|
|
||||||
waypoint.name = "SPLIT"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def ingress(
|
def ingress(
|
||||||
self,
|
self,
|
||||||
@ -229,33 +235,37 @@ class WaypointBuilder:
|
|||||||
position: Point,
|
position: Point,
|
||||||
objective: MissionTarget,
|
objective: MissionTarget,
|
||||||
) -> FlightWaypoint:
|
) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
if self.is_helo:
|
||||||
|
alt_type = "RADIO"
|
||||||
|
|
||||||
|
return FlightWaypoint(
|
||||||
|
"INGRESS",
|
||||||
ingress_type,
|
ingress_type,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
||||||
|
alt_type,
|
||||||
|
description=f"INGRESS on {objective.name}",
|
||||||
|
pretty_name=f"INGRESS on {objective.name}",
|
||||||
|
targets=objective.strike_targets,
|
||||||
)
|
)
|
||||||
if self.is_helo:
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.pretty_name = "INGRESS on " + objective.name
|
|
||||||
waypoint.description = "INGRESS on " + objective.name
|
|
||||||
waypoint.name = "INGRESS"
|
|
||||||
waypoint.targets = objective.strike_targets
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint:
|
def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
if self.is_helo:
|
||||||
|
alt_type = "RADIO"
|
||||||
|
|
||||||
|
return FlightWaypoint(
|
||||||
|
"EGRESS",
|
||||||
FlightWaypointType.EGRESS,
|
FlightWaypointType.EGRESS,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
||||||
|
alt_type,
|
||||||
|
description=f"EGRESS from {target.name}",
|
||||||
|
pretty_name=f"EGRESS from {target.name}",
|
||||||
)
|
)
|
||||||
if self.is_helo:
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.pretty_name = "EGRESS from " + target.name
|
|
||||||
waypoint.description = "EGRESS from " + target.name
|
|
||||||
waypoint.name = "EGRESS"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def bai_group(self, target: StrikeTarget) -> FlightWaypoint:
|
def bai_group(self, target: StrikeTarget) -> FlightWaypoint:
|
||||||
return self._target_point(target, f"ATTACK {target.name}")
|
return self._target_point(target, f"ATTACK {target.name}")
|
||||||
@ -271,21 +281,20 @@ class WaypointBuilder:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _target_point(target: StrikeTarget, description: str) -> FlightWaypoint:
|
def _target_point(target: StrikeTarget, description: str) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
target.name,
|
||||||
FlightWaypointType.TARGET_POINT,
|
FlightWaypointType.TARGET_POINT,
|
||||||
target.target.position.x,
|
target.target.position.x,
|
||||||
target.target.position.y,
|
target.target.position.y,
|
||||||
meters(0),
|
meters(0),
|
||||||
|
"RADIO",
|
||||||
|
description=description,
|
||||||
|
pretty_name=description,
|
||||||
|
# The target waypoints are only for the player's benefit. AI tasks for
|
||||||
|
# the target are set on the ingress point so that they begin their attack
|
||||||
|
# *before* reaching the target.
|
||||||
|
only_for_player=True,
|
||||||
)
|
)
|
||||||
waypoint.description = description
|
|
||||||
waypoint.pretty_name = description
|
|
||||||
waypoint.name = target.name
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
# The target waypoints are only for the player's benefit. AI tasks for
|
|
||||||
# the target are set on the ingress point so they begin their attack
|
|
||||||
# *before* reaching the target.
|
|
||||||
waypoint.only_for_player = True
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def strike_area(self, target: MissionTarget) -> FlightWaypoint:
|
def strike_area(self, target: MissionTarget) -> FlightWaypoint:
|
||||||
return self._target_area(f"STRIKE {target.name}", target)
|
return self._target_area(f"STRIKE {target.name}", target)
|
||||||
@ -304,15 +313,15 @@ class WaypointBuilder:
|
|||||||
name: str, location: MissionTarget, flyover: bool = False
|
name: str, location: MissionTarget, flyover: bool = False
|
||||||
) -> FlightWaypoint:
|
) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
waypoint = FlightWaypoint(
|
||||||
|
name,
|
||||||
FlightWaypointType.TARGET_GROUP_LOC,
|
FlightWaypointType.TARGET_GROUP_LOC,
|
||||||
location.position.x,
|
location.position.x,
|
||||||
location.position.y,
|
location.position.y,
|
||||||
meters(0),
|
meters(0),
|
||||||
|
"RADIO",
|
||||||
|
description=name,
|
||||||
|
pretty_name=name,
|
||||||
)
|
)
|
||||||
waypoint.description = name
|
|
||||||
waypoint.pretty_name = name
|
|
||||||
waypoint.name = name
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
|
|
||||||
# Most target waypoints are only for the player's benefit. AI tasks for
|
# Most target waypoints are only for the player's benefit. AI tasks for
|
||||||
# the target are set on the ingress point so they begin their attack
|
# the target are set on the ingress point so they begin their attack
|
||||||
@ -328,17 +337,16 @@ class WaypointBuilder:
|
|||||||
return waypoint
|
return waypoint
|
||||||
|
|
||||||
def cas(self, position: Point) -> FlightWaypoint:
|
def cas(self, position: Point) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"CAS",
|
||||||
FlightWaypointType.CAS,
|
FlightWaypointType.CAS,
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
meters(60) if self.is_helo else meters(1000),
|
meters(60) if self.is_helo else meters(1000),
|
||||||
|
"RADIO",
|
||||||
|
description="Provide CAS",
|
||||||
|
pretty_name="CAS",
|
||||||
)
|
)
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.description = "Provide CAS"
|
|
||||||
waypoint.name = "CAS"
|
|
||||||
waypoint.pretty_name = "CAS"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def race_track_start(position: Point, altitude: Distance) -> FlightWaypoint:
|
def race_track_start(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||||
@ -348,13 +356,15 @@ class WaypointBuilder:
|
|||||||
position: Position of the waypoint.
|
position: Position of the waypoint.
|
||||||
altitude: Altitude of the racetrack.
|
altitude: Altitude of the racetrack.
|
||||||
"""
|
"""
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
FlightWaypointType.PATROL_TRACK, position.x, position.y, altitude
|
"RACETRACK START",
|
||||||
|
FlightWaypointType.PATROL_TRACK,
|
||||||
|
position.x,
|
||||||
|
position.y,
|
||||||
|
altitude,
|
||||||
|
description="Orbit between this point and the next point",
|
||||||
|
pretty_name="Race-track start",
|
||||||
)
|
)
|
||||||
waypoint.name = "RACETRACK START"
|
|
||||||
waypoint.description = "Orbit between this point and the next point"
|
|
||||||
waypoint.pretty_name = "Race-track start"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def race_track_end(position: Point, altitude: Distance) -> FlightWaypoint:
|
def race_track_end(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||||
@ -364,13 +374,15 @@ class WaypointBuilder:
|
|||||||
position: Position of the waypoint.
|
position: Position of the waypoint.
|
||||||
altitude: Altitude of the racetrack.
|
altitude: Altitude of the racetrack.
|
||||||
"""
|
"""
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
FlightWaypointType.PATROL, position.x, position.y, altitude
|
"RACETRACK END",
|
||||||
|
FlightWaypointType.PATROL,
|
||||||
|
position.x,
|
||||||
|
position.y,
|
||||||
|
altitude,
|
||||||
|
description="Orbit between this point and the previous point",
|
||||||
|
pretty_name="Race-track end",
|
||||||
)
|
)
|
||||||
waypoint.name = "RACETRACK END"
|
|
||||||
waypoint.description = "Orbit between this point and the previous point"
|
|
||||||
waypoint.pretty_name = "Race-track end"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def race_track(
|
def race_track(
|
||||||
self, start: Point, end: Point, altitude: Distance
|
self, start: Point, end: Point, altitude: Distance
|
||||||
@ -396,11 +408,15 @@ class WaypointBuilder:
|
|||||||
altitude: Altitude of the racetrack.
|
altitude: Altitude of the racetrack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
waypoint = FlightWaypoint(FlightWaypointType.LOITER, start.x, start.y, altitude)
|
return FlightWaypoint(
|
||||||
waypoint.name = "ORBIT"
|
"ORBIT",
|
||||||
waypoint.description = "Anchor and hold at this point"
|
FlightWaypointType.LOITER,
|
||||||
waypoint.pretty_name = "Orbit"
|
start.x,
|
||||||
return waypoint
|
start.y,
|
||||||
|
altitude,
|
||||||
|
description="Anchor and hold at this point",
|
||||||
|
pretty_name="Orbit",
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def sweep_start(position: Point, altitude: Distance) -> FlightWaypoint:
|
def sweep_start(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||||
@ -410,13 +426,15 @@ class WaypointBuilder:
|
|||||||
position: Position of the waypoint.
|
position: Position of the waypoint.
|
||||||
altitude: Altitude of the sweep in meters.
|
altitude: Altitude of the sweep in meters.
|
||||||
"""
|
"""
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
FlightWaypointType.INGRESS_SWEEP, position.x, position.y, altitude
|
"SWEEP START",
|
||||||
|
FlightWaypointType.INGRESS_SWEEP,
|
||||||
|
position.x,
|
||||||
|
position.y,
|
||||||
|
altitude,
|
||||||
|
description="Proceed to the target and engage enemy aircraft",
|
||||||
|
pretty_name="Sweep start",
|
||||||
)
|
)
|
||||||
waypoint.name = "SWEEP START"
|
|
||||||
waypoint.description = "Proceed to the target and engage enemy aircraft"
|
|
||||||
waypoint.pretty_name = "Sweep start"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def sweep_end(position: Point, altitude: Distance) -> FlightWaypoint:
|
def sweep_end(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||||
@ -426,13 +444,15 @@ class WaypointBuilder:
|
|||||||
position: Position of the waypoint.
|
position: Position of the waypoint.
|
||||||
altitude: Altitude of the sweep in meters.
|
altitude: Altitude of the sweep in meters.
|
||||||
"""
|
"""
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
FlightWaypointType.EGRESS, position.x, position.y, altitude
|
"SWEEP END",
|
||||||
|
FlightWaypointType.EGRESS,
|
||||||
|
position.x,
|
||||||
|
position.y,
|
||||||
|
altitude,
|
||||||
|
description="End of sweep",
|
||||||
|
pretty_name="Sweep end",
|
||||||
)
|
)
|
||||||
waypoint.name = "SWEEP END"
|
|
||||||
waypoint.description = "End of sweep"
|
|
||||||
waypoint.pretty_name = "Sweep end"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
def sweep(
|
def sweep(
|
||||||
self, start: Point, end: Point, altitude: Distance
|
self, start: Point, end: Point, altitude: Distance
|
||||||
@ -457,6 +477,10 @@ class WaypointBuilder:
|
|||||||
ingress: The package ingress point.
|
ingress: The package ingress point.
|
||||||
target: The mission target.
|
target: The mission target.
|
||||||
"""
|
"""
|
||||||
|
alt_type: AltitudeReference = "BARO"
|
||||||
|
if self.is_helo:
|
||||||
|
alt_type = "RADIO"
|
||||||
|
|
||||||
# This would preferably be no points at all, and instead the Escort task
|
# This would preferably be no points at all, and instead the Escort task
|
||||||
# would begin on the join point and end on the split point, however the
|
# would begin on the join point and end on the split point, however the
|
||||||
# escort task does not appear to work properly (see the longer
|
# escort task does not appear to work properly (see the longer
|
||||||
@ -464,18 +488,16 @@ class WaypointBuilder:
|
|||||||
# the escort flights a flight plan including the ingress point and target area.
|
# the escort flights a flight plan including the ingress point and target area.
|
||||||
ingress_wp = self.ingress(FlightWaypointType.INGRESS_ESCORT, ingress, target)
|
ingress_wp = self.ingress(FlightWaypointType.INGRESS_ESCORT, ingress, target)
|
||||||
|
|
||||||
waypoint = FlightWaypoint(
|
return ingress_wp, FlightWaypoint(
|
||||||
|
"TARGET",
|
||||||
FlightWaypointType.TARGET_GROUP_LOC,
|
FlightWaypointType.TARGET_GROUP_LOC,
|
||||||
target.position.x,
|
target.position.x,
|
||||||
target.position.y,
|
target.position.y,
|
||||||
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
||||||
|
alt_type,
|
||||||
|
description="Escort the package",
|
||||||
|
pretty_name="Target area",
|
||||||
)
|
)
|
||||||
if self.is_helo:
|
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.name = "TARGET"
|
|
||||||
waypoint.description = "Escort the package"
|
|
||||||
waypoint.pretty_name = "Target area"
|
|
||||||
return ingress_wp, waypoint
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pickup(control_point: ControlPoint) -> FlightWaypoint:
|
def pickup(control_point: ControlPoint) -> FlightWaypoint:
|
||||||
@ -484,17 +506,16 @@ class WaypointBuilder:
|
|||||||
Args:
|
Args:
|
||||||
control_point: Pick up location.
|
control_point: Pick up location.
|
||||||
"""
|
"""
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"PICKUP",
|
||||||
FlightWaypointType.PICKUP,
|
FlightWaypointType.PICKUP,
|
||||||
control_point.position.x,
|
control_point.position.x,
|
||||||
control_point.position.y,
|
control_point.position.y,
|
||||||
meters(0),
|
meters(0),
|
||||||
|
"RADIO",
|
||||||
|
description=f"Pick up cargo from {control_point}",
|
||||||
|
pretty_name="Pick up location",
|
||||||
)
|
)
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.name = "PICKUP"
|
|
||||||
waypoint.description = f"Pick up cargo from {control_point}"
|
|
||||||
waypoint.pretty_name = "Pick up location"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def drop_off(control_point: ControlPoint) -> FlightWaypoint:
|
def drop_off(control_point: ControlPoint) -> FlightWaypoint:
|
||||||
@ -503,18 +524,17 @@ class WaypointBuilder:
|
|||||||
Args:
|
Args:
|
||||||
control_point: Drop-off location.
|
control_point: Drop-off location.
|
||||||
"""
|
"""
|
||||||
waypoint = FlightWaypoint(
|
return FlightWaypoint(
|
||||||
|
"DROP OFF",
|
||||||
FlightWaypointType.PICKUP,
|
FlightWaypointType.PICKUP,
|
||||||
control_point.position.x,
|
control_point.position.x,
|
||||||
control_point.position.y,
|
control_point.position.y,
|
||||||
meters(0),
|
meters(0),
|
||||||
|
"RADIO",
|
||||||
|
description=f"Drop off cargo at {control_point}",
|
||||||
|
pretty_name="Drop off location",
|
||||||
control_point=control_point,
|
control_point=control_point,
|
||||||
)
|
)
|
||||||
waypoint.alt_type = "RADIO"
|
|
||||||
waypoint.name = "DROP OFF"
|
|
||||||
waypoint.description = f"Drop off cargo at {control_point}"
|
|
||||||
waypoint.pretty_name = "Drop off location"
|
|
||||||
return waypoint
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def nav(
|
def nav(
|
||||||
@ -527,15 +547,20 @@ class WaypointBuilder:
|
|||||||
altitude: Altitude of the waypoint.
|
altitude: Altitude of the waypoint.
|
||||||
altitude_is_agl: True for altitude is AGL. False if altitude is MSL.
|
altitude_is_agl: True for altitude is AGL. False if altitude is MSL.
|
||||||
"""
|
"""
|
||||||
waypoint = FlightWaypoint(
|
alt_type: AltitudeReference = "BARO"
|
||||||
FlightWaypointType.NAV, position.x, position.y, altitude
|
|
||||||
)
|
|
||||||
if altitude_is_agl:
|
if altitude_is_agl:
|
||||||
waypoint.alt_type = "RADIO"
|
alt_type = "RADIO"
|
||||||
waypoint.name = "NAV"
|
|
||||||
waypoint.description = "NAV"
|
return FlightWaypoint(
|
||||||
waypoint.pretty_name = "Nav"
|
"NAV",
|
||||||
return waypoint
|
FlightWaypointType.NAV,
|
||||||
|
position.x,
|
||||||
|
position.y,
|
||||||
|
altitude,
|
||||||
|
alt_type,
|
||||||
|
description="NAV",
|
||||||
|
pretty_name="Nav",
|
||||||
|
)
|
||||||
|
|
||||||
def nav_path(
|
def nav_path(
|
||||||
self, a: Point, b: Point, altitude: Distance, altitude_is_agl: bool = False
|
self, a: Point, b: Point, altitude: Distance, altitude_is_agl: bool = False
|
||||||
|
|||||||
@ -768,7 +768,7 @@ class Waypoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
position() {
|
position() {
|
||||||
return this.waypoint.latlng;
|
return this.waypoint.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldMark() {
|
shouldMark() {
|
||||||
@ -783,10 +783,8 @@ class Waypoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async description(dragging) {
|
async description(dragging) {
|
||||||
const alt = Math.floor(
|
const alt = this.waypoint.altitude_ft;
|
||||||
this.waypoint.alt.distance_in_meters * METERS_TO_FEET
|
const altRef = this.waypoint.altitude_reference;
|
||||||
);
|
|
||||||
const altRef = this.waypoint.alt_type == "BARO" ? "MSL" : "AGL";
|
|
||||||
return (
|
return (
|
||||||
`${this.number} ${this.waypoint.name}<br />` +
|
`${this.number} ${this.waypoint.name}<br />` +
|
||||||
`${alt} ft ${altRef}<br />` +
|
`${alt} ft ${altRef}<br />` +
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user