diff --git a/game/ato/flightplans/escort.py b/game/ato/flightplans/escort.py index f07d3e63..1884de21 100644 --- a/game/ato/flightplans/escort.py +++ b/game/ato/flightplans/escort.py @@ -8,6 +8,8 @@ from .formationattack import ( FormationAttackLayout, ) from .waypointbuilder import WaypointBuilder +from .. import FlightType +from ...utils import Distance class EscortFlightPlan(FormationAttackFlightPlan): @@ -24,12 +26,17 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]): ingress, target = builder.escort( self.package.waypoints.ingress, self.package.target ) + ingress.only_for_player = True + target.only_for_player = True hold = builder.hold(self._hold_point()) join = builder.join(self.package.waypoints.join) split = builder.split(self.package.waypoints.split) refuel = None if self.package.waypoints.refuel is not None: refuel = builder.refuel(self.package.waypoints.refuel) + initial = None + if self.package.primary_task == FlightType.STRIKE: + initial = builder.escort_hold(self.package.waypoints.initial, Distance.from_feet(20000)) return FormationAttackLayout( departure=builder.takeoff(self.flight.departure), @@ -39,6 +46,7 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]): ), join=join, ingress=ingress, + initial=initial, targets=[target], split=split, refuel=refuel, diff --git a/game/ato/flightplans/formationattack.py b/game/ato/flightplans/formationattack.py index 9a0c6ed3..23b240a0 100644 --- a/game/ato/flightplans/formationattack.py +++ b/game/ato/flightplans/formationattack.py @@ -4,13 +4,13 @@ from abc import ABC from collections.abc import Iterator from dataclasses import dataclass from datetime import timedelta -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING, TypeVar, Optional from dcs import Point from game.flightplan import HoldZoneGeometry from game.theater import MissionTarget -from game.utils import Speed, meters +from game.utils import Speed, meters, Distance from .flightplan import FlightPlan from .formation import FormationFlightPlan, FormationLayout from .ibuilder import IBuilder @@ -134,6 +134,7 @@ class FormationAttackFlightPlan(FormationFlightPlan, ABC): class FormationAttackLayout(FormationLayout): ingress: FlightWaypoint targets: list[FlightWaypoint] + initial: Optional[FlightWaypoint] = None def iter_waypoints(self) -> Iterator[FlightWaypoint]: yield self.departure @@ -141,6 +142,8 @@ class FormationAttackLayout(FormationLayout): yield from self.nav_to yield self.join yield self.ingress + if self.initial is not None: + yield self.initial yield from self.targets yield self.split if self.refuel is not None: @@ -185,6 +188,16 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC): if self.package.waypoints.refuel is not None: refuel = builder.refuel(self.package.waypoints.refuel) + ingress = builder.ingress( + ingress_type, self.package.waypoints.ingress, self.package.target + ) + initial = None + if self.package.primary_task == FlightType.STRIKE: + ingress = builder.nav(self.package.waypoints.ingress, Distance.from_feet(20000)) + initial = builder.ingress( + ingress_type, self.package.waypoints.initial, self.package.target + ) + return FormationAttackLayout( departure=builder.takeoff(self.flight.departure), hold=hold, @@ -192,9 +205,8 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC): hold.position, join.position, self.doctrine.ingress_altitude ), join=join, - ingress=builder.ingress( - ingress_type, self.package.waypoints.ingress, self.package.target - ), + ingress=ingress, + initial=initial, targets=target_waypoints, split=split, refuel=refuel, diff --git a/game/ato/flightplans/waypointbuilder.py b/game/ato/flightplans/waypointbuilder.py index 232d48a4..e5ac9367 100644 --- a/game/ato/flightplans/waypointbuilder.py +++ b/game/ato/flightplans/waypointbuilder.py @@ -415,6 +415,24 @@ class WaypointBuilder: pretty_name="Orbit", ) + @staticmethod + def escort_hold(start: Point, altitude: Distance) -> FlightWaypoint: + """Creates custom waypoint for escort flights that need to hold. + + Args: + start: Position of the waypoint. + altitude: Altitude of the holding pattern. + """ + + return FlightWaypoint( + "ESCORT HOLD", + FlightWaypointType.CUSTOM, + start, + altitude, + description="Anchor and hold at this point", + pretty_name="Escort Hold", + ) + @staticmethod def sweep_start(position: Point, altitude: Distance) -> FlightWaypoint: """Creates a sweep start waypoint. diff --git a/game/ato/packagewaypoints.py b/game/ato/packagewaypoints.py index 62bd4219..d751ebaa 100644 --- a/game/ato/packagewaypoints.py +++ b/game/ato/packagewaypoints.py @@ -1,13 +1,15 @@ from __future__ import annotations +import random from dataclasses import dataclass -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from dcs import Point from game.ato.flightplans.waypointbuilder import WaypointBuilder from game.flightplan import IpZoneGeometry, JoinZoneGeometry from game.flightplan.refuelzonegeometry import RefuelZoneGeometry +from game.utils import nautical_miles if TYPE_CHECKING: from game.ato import Package @@ -18,6 +20,7 @@ if TYPE_CHECKING: class PackageWaypoints: join: Point ingress: Point + initial: Point split: Point refuel: Point @@ -32,6 +35,10 @@ class PackageWaypoints: coalition, ).find_best_ip() + hdg = package.target.position.heading_between_point(ingress_point) + dist = nautical_miles(random.random() * 2 + 7).meters + initial_point = package.target.position.point_from_heading(hdg, dist) + join_point = JoinZoneGeometry( package.target.position, origin.position, @@ -51,6 +58,7 @@ class PackageWaypoints: return PackageWaypoints( WaypointBuilder.perturb(join_point), ingress_point, + initial_point, WaypointBuilder.perturb(join_point), refuel_point, )