Add a real CAS ingress point.

Putting the ingress point directly on one end of the FLOT means that AI
flights won't start searching and engaging targets until they reach that
point. If the front line has advanced toward the flight's departure
airfield, it might overfly targets on its way to the IP.

Instead, place an IP for CAS the same way we place any other IP. The AI
will fly to that and start searching from there.

This also:

* Removes the midpoint waypoint, since it didn't serve any real purpose
* Names the FLOT boundary waypoints for what they actually are

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2231.
This commit is contained in:
Dan Albert 2023-08-10 00:03:30 -07:00
parent 9460586cfe
commit cb3bf56d84
28 changed files with 100 additions and 87 deletions

View File

@ -7,6 +7,7 @@ Saves from 8.x are not compatible with 9.0.0.
* **[Data]** Added support for the ARA Veinticinco de Mayo. * **[Data]** Added support for the ARA Veinticinco de Mayo.
* **[Data]** Changed display name of the AI-only F-15E Strike Eagle for clarity. * **[Data]** Changed display name of the AI-only F-15E Strike Eagle for clarity.
* **[Flight Planning]** Improved IP selection for targets that are near the center of a threat zone. * **[Flight Planning]** Improved IP selection for targets that are near the center of a threat zone.
* **[Flight Planning]** Moved CAS ingress point off the front line so that the AI begins their target search earlier.
* **[Flight Planning]** Loadouts and aircraft properties can now be set per-flight member. Warning: AI flights should not use mixed loadouts. * **[Flight Planning]** Loadouts and aircraft properties can now be set per-flight member. Warning: AI flights should not use mixed loadouts.
* **[Flight Planning]** Laser codes that are pre-assigned to weapons at mission start can now be chosen from a list in the loadout UI. This does not affect the aircraft's TGP, just the weapons. Currently only implemented for the F-15E S4+ and F-16C. * **[Flight Planning]** Laser codes that are pre-assigned to weapons at mission start can now be chosen from a list in the loadout UI. This does not affect the aircraft's TGP, just the weapons. Currently only implemented for the F-15E S4+ and F-16C.
* **[Mission Generation]** Configured target and initial points for F-15E S4+. * **[Mission Generation]** Configured target and initial points for F-15E S4+.

View File

@ -90,5 +90,5 @@ class Builder(IBuilder[AewcFlightPlan, PatrollingLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> AewcFlightPlan: def build(self, dump_debug_info: bool = False) -> AewcFlightPlan:
return AewcFlightPlan(self.flight, self.layout()) return AewcFlightPlan(self.flight, self.layout())

View File

@ -152,5 +152,5 @@ class Builder(IBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> AirAssaultFlightPlan: def build(self, dump_debug_info: bool = False) -> AirAssaultFlightPlan:
return AirAssaultFlightPlan(self.flight, self.layout()) return AirAssaultFlightPlan(self.flight, self.layout())

View File

@ -155,5 +155,5 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> AirliftFlightPlan: def build(self, dump_debug_info: bool = False) -> AirliftFlightPlan:
return AirliftFlightPlan(self.flight, self.layout()) return AirliftFlightPlan(self.flight, self.layout())

View File

@ -41,5 +41,5 @@ class Builder(FormationAttackBuilder[AntiShipFlightPlan, FormationAttackLayout])
def anti_ship_targets_for_tgo(tgo: NavalGroundObject) -> list[StrikeTarget]: def anti_ship_targets_for_tgo(tgo: NavalGroundObject) -> list[StrikeTarget]:
return [StrikeTarget(f"{g.group_name} at {tgo.name}", g) for g in tgo.groups] return [StrikeTarget(f"{g.group_name} at {tgo.name}", g) for g in tgo.groups]
def build(self) -> AntiShipFlightPlan: def build(self, dump_debug_info: bool = False) -> AntiShipFlightPlan:
return AntiShipFlightPlan(self.flight, self.layout()) return AntiShipFlightPlan(self.flight, self.layout())

View File

@ -39,5 +39,5 @@ class Builder(FormationAttackBuilder[BaiFlightPlan, FormationAttackLayout]):
return self._build(FlightWaypointType.INGRESS_BAI, targets) return self._build(FlightWaypointType.INGRESS_BAI, targets)
def build(self) -> BaiFlightPlan: def build(self, dump_debug_info: bool = False) -> BaiFlightPlan:
return BaiFlightPlan(self.flight, self.layout()) return BaiFlightPlan(self.flight, self.layout())

View File

@ -66,5 +66,5 @@ class Builder(CapBuilder[BarCapFlightPlan, PatrollingLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> BarCapFlightPlan: def build(self, dump_debug_info: bool = False) -> BarCapFlightPlan:
return BarCapFlightPlan(self.flight, self.layout()) return BarCapFlightPlan(self.flight, self.layout())

View File

@ -6,13 +6,15 @@ from datetime import timedelta
from typing import TYPE_CHECKING, Type from typing import TYPE_CHECKING, Type
from game.theater import FrontLine from game.theater import FrontLine
from game.utils import Distance, Speed, kph, meters from game.utils import Distance, Speed, kph, meters, dcs_to_shapely_point
from .ibuilder import IBuilder from .ibuilder import IBuilder
from .invalidobjectivelocation import InvalidObjectiveLocation from .invalidobjectivelocation import InvalidObjectiveLocation
from .patrolling import PatrollingFlightPlan, PatrollingLayout from .patrolling import PatrollingFlightPlan, PatrollingLayout
from .uizonedisplay import UiZone, UiZoneDisplay from .uizonedisplay import UiZone, UiZoneDisplay
from .waypointbuilder import WaypointBuilder from .waypointbuilder import WaypointBuilder
from ..flightwaypointtype import FlightWaypointType from ..flightwaypointtype import FlightWaypointType
from ...flightplan.ipsolver import IpSolver
from ...persistence.paths import waypoint_debug_directory
if TYPE_CHECKING: if TYPE_CHECKING:
from ..flightwaypoint import FlightWaypoint from ..flightwaypoint import FlightWaypoint
@ -20,13 +22,13 @@ if TYPE_CHECKING:
@dataclass(frozen=True) @dataclass(frozen=True)
class CasLayout(PatrollingLayout): class CasLayout(PatrollingLayout):
target: FlightWaypoint ingress: FlightWaypoint
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.departure yield self.departure
yield from self.nav_to yield from self.nav_to
yield self.ingress
yield self.patrol_start yield self.patrol_start
yield self.target
yield self.patrol_end yield self.patrol_end
yield from self.nav_from yield from self.nav_from
yield self.arrival yield self.arrival
@ -59,7 +61,7 @@ class CasFlightPlan(PatrollingFlightPlan[CasLayout], UiZoneDisplay):
@property @property
def combat_speed_waypoints(self) -> set[FlightWaypoint]: def combat_speed_waypoints(self) -> set[FlightWaypoint]:
return {self.layout.patrol_start, self.layout.target, self.layout.patrol_end} return {self.layout.ingress, self.layout.patrol_start, self.layout.patrol_end}
def request_escort_at(self) -> FlightWaypoint | None: def request_escort_at(self) -> FlightWaypoint | None:
return self.layout.patrol_start return self.layout.patrol_start
@ -68,14 +70,17 @@ class CasFlightPlan(PatrollingFlightPlan[CasLayout], UiZoneDisplay):
return self.layout.patrol_end return self.layout.patrol_end
def ui_zone(self) -> UiZone: def ui_zone(self) -> UiZone:
midpoint = (
self.layout.patrol_start.position + self.layout.patrol_end.position
) / 2
return UiZone( return UiZone(
[self.layout.target.position], [midpoint],
self.engagement_distance, self.engagement_distance,
) )
class Builder(IBuilder[CasFlightPlan, CasLayout]): class Builder(IBuilder[CasFlightPlan, CasLayout]):
def layout(self) -> CasLayout: def layout(self, dump_debug_info: bool) -> CasLayout:
location = self.package.target location = self.package.target
if not isinstance(location, FrontLine): if not isinstance(location, FrontLine):
@ -86,46 +91,77 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
) )
bounds = FrontLineConflictDescription.frontline_bounds(location, self.theater) bounds = FrontLineConflictDescription.frontline_bounds(location, self.theater)
ingress = bounds.left_position patrol_start = bounds.left_position
center = bounds.center patrol_end = bounds.right_position
egress = bounds.right_position
ingress_distance = ingress.distance_to_point(self.flight.departure.position) start_distance = patrol_start.distance_to_point(self.flight.departure.position)
egress_distance = egress.distance_to_point(self.flight.departure.position) end_distance = patrol_end.distance_to_point(self.flight.departure.position)
if egress_distance < ingress_distance: if end_distance < start_distance:
ingress, egress = egress, ingress patrol_start, patrol_end = patrol_end, patrol_start
builder = WaypointBuilder(self.flight, self.coalition) builder = WaypointBuilder(self.flight, self.coalition)
is_helo = self.flight.unit_type.dcs_unit_type.helicopter is_helo = self.flight.unit_type.dcs_unit_type.helicopter
ingress_egress_altitude = ( patrol_altitude = self.doctrine.ingress_altitude if not is_helo else meters(50)
self.doctrine.ingress_altitude if not is_helo else meters(50) use_agl_patrol_altitude = is_helo
ip_solver = IpSolver(
dcs_to_shapely_point(self.flight.departure.position),
dcs_to_shapely_point(patrol_start),
self.doctrine,
self.threat_zones.all,
) )
use_agl_ingress_egress = is_helo ip_solver.set_debug_properties(
waypoint_debug_directory() / "IP", self.theater.terrain
)
ingress_point_shapely = ip_solver.solve()
if dump_debug_info:
ip_solver.dump_debug_info()
ingress_point = patrol_start.new_in_same_map(
ingress_point_shapely.x, ingress_point_shapely.y
)
patrol_start_waypoint = builder.nav(
patrol_start, patrol_altitude, use_agl_patrol_altitude
)
patrol_start_waypoint.name = "FLOT START"
patrol_start_waypoint.pretty_name = "FLOT start"
patrol_start_waypoint.description = "FLOT boundary"
patrol_end_waypoint = builder.nav(
patrol_end, patrol_altitude, use_agl_patrol_altitude
)
patrol_end_waypoint.name = "FLOT END"
patrol_end_waypoint.pretty_name = "FLOT end"
patrol_end_waypoint.description = "FLOT boundary"
ingress = builder.ingress(
FlightWaypointType.INGRESS_CAS, ingress_point, location
)
ingress.description = f"Ingress to provide CAS at {location}"
return CasLayout( return CasLayout(
departure=builder.takeoff(self.flight.departure), departure=builder.takeoff(self.flight.departure),
nav_to=builder.nav_path( nav_to=builder.nav_path(
self.flight.departure.position, self.flight.departure.position,
ingress, ingress_point,
ingress_egress_altitude, patrol_altitude,
use_agl_ingress_egress, use_agl_patrol_altitude,
), ),
nav_from=builder.nav_path( nav_from=builder.nav_path(
egress, patrol_end,
self.flight.arrival.position, self.flight.arrival.position,
ingress_egress_altitude, patrol_altitude,
use_agl_ingress_egress, use_agl_patrol_altitude,
), ),
patrol_start=builder.ingress( ingress=ingress,
FlightWaypointType.INGRESS_CAS, ingress, location patrol_start=patrol_start_waypoint,
), patrol_end=patrol_end_waypoint,
target=builder.cas(center),
patrol_end=builder.egress(egress, location),
arrival=builder.land(self.flight.arrival), arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert), divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> CasFlightPlan: def build(self, dump_debug_info: bool = False) -> CasFlightPlan:
return CasFlightPlan(self.flight, self.layout()) return CasFlightPlan(self.flight, self.layout(dump_debug_info))

View File

@ -72,5 +72,5 @@ class Builder(IBuilder[CustomFlightPlan, CustomLayout]):
builder = WaypointBuilder(self.flight, self.coalition) builder = WaypointBuilder(self.flight, self.coalition)
return CustomLayout(builder.takeoff(self.flight.departure), self.waypoints) return CustomLayout(builder.takeoff(self.flight.departure), self.waypoints)
def build(self) -> CustomFlightPlan: def build(self, dump_debug_info: bool = False) -> CustomFlightPlan:
return CustomFlightPlan(self.flight, self.layout()) return CustomFlightPlan(self.flight, self.layout())

View File

@ -37,5 +37,5 @@ class Builder(FormationAttackBuilder[DeadFlightPlan, FormationAttackLayout]):
return self._build(FlightWaypointType.INGRESS_DEAD) return self._build(FlightWaypointType.INGRESS_DEAD)
def build(self) -> DeadFlightPlan: def build(self, dump_debug_info: bool = False) -> DeadFlightPlan:
return DeadFlightPlan(self.flight, self.layout()) return DeadFlightPlan(self.flight, self.layout())

View File

@ -50,5 +50,5 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> EscortFlightPlan: def build(self, dump_debug_info: bool = False) -> EscortFlightPlan:
return EscortFlightPlan(self.flight, self.layout()) return EscortFlightPlan(self.flight, self.layout())

View File

@ -83,5 +83,5 @@ class Builder(IBuilder[FerryFlightPlan, FerryLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> FerryFlightPlan: def build(self, dump_debug_info: bool = False) -> FerryFlightPlan:
return FerryFlightPlan(self.flight, self.layout()) return FerryFlightPlan(self.flight, self.layout())

View File

@ -35,7 +35,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
def regenerate(self, dump_debug_info: bool = False) -> None: def regenerate(self, dump_debug_info: bool = False) -> None:
try: try:
self._generate_package_waypoints_if_needed(dump_debug_info) self._generate_package_waypoints_if_needed(dump_debug_info)
self._flight_plan = self.build() self._flight_plan = self.build(dump_debug_info)
except NavMeshError as ex: except NavMeshError as ex:
color = "blue" if self.flight.squadron.player else "red" color = "blue" if self.flight.squadron.player else "red"
raise PlanningError( raise PlanningError(
@ -59,11 +59,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
return self.flight.departure.theater return self.flight.departure.theater
@abstractmethod @abstractmethod
def layout(self) -> LayoutT: def build(self, dump_debug_info: bool = False) -> FlightPlanT:
...
@abstractmethod
def build(self) -> FlightPlanT:
... ...
@property @property

View File

@ -32,5 +32,5 @@ class Builder(FormationAttackBuilder[OcaAircraftFlightPlan, FormationAttackLayou
return self._build(FlightWaypointType.INGRESS_OCA_AIRCRAFT) return self._build(FlightWaypointType.INGRESS_OCA_AIRCRAFT)
def build(self) -> OcaAircraftFlightPlan: def build(self, dump_debug_info: bool = False) -> OcaAircraftFlightPlan:
return OcaAircraftFlightPlan(self.flight, self.layout()) return OcaAircraftFlightPlan(self.flight, self.layout())

View File

@ -32,5 +32,5 @@ class Builder(FormationAttackBuilder[OcaRunwayFlightPlan, FormationAttackLayout]
return self._build(FlightWaypointType.INGRESS_OCA_RUNWAY) return self._build(FlightWaypointType.INGRESS_OCA_RUNWAY)
def build(self) -> OcaRunwayFlightPlan: def build(self, dump_debug_info: bool = False) -> OcaRunwayFlightPlan:
return OcaRunwayFlightPlan(self.flight, self.layout()) return OcaRunwayFlightPlan(self.flight, self.layout())

View File

@ -121,5 +121,5 @@ class Builder(IBuilder[PackageRefuelingFlightPlan, PatrollingLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> PackageRefuelingFlightPlan: def build(self, dump_debug_info: bool = False) -> PackageRefuelingFlightPlan:
return PackageRefuelingFlightPlan(self.flight, self.layout()) return PackageRefuelingFlightPlan(self.flight, self.layout())

View File

@ -93,5 +93,5 @@ class Builder(IBuilder[RtbFlightPlan, RtbLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> RtbFlightPlan: def build(self, dump_debug_info: bool = False) -> RtbFlightPlan:
return RtbFlightPlan(self.flight, self.layout()) return RtbFlightPlan(self.flight, self.layout())

View File

@ -24,5 +24,5 @@ class Builder(FormationAttackBuilder[SeadFlightPlan, FormationAttackLayout]):
def layout(self) -> FormationAttackLayout: def layout(self) -> FormationAttackLayout:
return self._build(FlightWaypointType.INGRESS_SEAD) return self._build(FlightWaypointType.INGRESS_SEAD)
def build(self) -> SeadFlightPlan: def build(self, dump_debug_info: bool = False) -> SeadFlightPlan:
return SeadFlightPlan(self.flight, self.layout()) return SeadFlightPlan(self.flight, self.layout())

View File

@ -91,5 +91,5 @@ class Builder(IBuilder[RecoveryTankerFlightPlan, RecoveryTankerLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> RecoveryTankerFlightPlan: def build(self, dump_debug_info: bool = False) -> RecoveryTankerFlightPlan:
return RecoveryTankerFlightPlan(self.flight, self.layout()) return RecoveryTankerFlightPlan(self.flight, self.layout())

View File

@ -32,5 +32,5 @@ class Builder(FormationAttackBuilder[StrikeFlightPlan, FormationAttackLayout]):
return self._build(FlightWaypointType.INGRESS_STRIKE, targets) return self._build(FlightWaypointType.INGRESS_STRIKE, targets)
def build(self) -> StrikeFlightPlan: def build(self, dump_debug_info: bool = False) -> StrikeFlightPlan:
return StrikeFlightPlan(self.flight, self.layout()) return StrikeFlightPlan(self.flight, self.layout())

View File

@ -137,5 +137,5 @@ class Builder(IBuilder[SweepFlightPlan, SweepLayout]):
target, origin, ip, join, self.coalition, self.theater target, origin, ip, join, self.coalition, self.theater
).find_best_hold_point() ).find_best_hold_point()
def build(self) -> SweepFlightPlan: def build(self, dump_debug_info: bool = False) -> SweepFlightPlan:
return SweepFlightPlan(self.flight, self.layout()) return SweepFlightPlan(self.flight, self.layout())

View File

@ -122,5 +122,5 @@ class Builder(CapBuilder[TarCapFlightPlan, TarCapLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> TarCapFlightPlan: def build(self, dump_debug_info: bool = False) -> TarCapFlightPlan:
return TarCapFlightPlan(self.flight, self.layout()) return TarCapFlightPlan(self.flight, self.layout())

View File

@ -79,5 +79,5 @@ class Builder(IBuilder[TheaterRefuelingFlightPlan, PatrollingLayout]):
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
) )
def build(self) -> TheaterRefuelingFlightPlan: def build(self, dump_debug_info: bool = False) -> TheaterRefuelingFlightPlan:
return TheaterRefuelingFlightPlan(self.flight, self.layout()) return TheaterRefuelingFlightPlan(self.flight, self.layout())

View File

@ -253,21 +253,6 @@ class WaypointBuilder:
targets=objective.strike_targets, targets=objective.strike_targets,
) )
def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
if self.is_helo:
alt_type = "RADIO"
return FlightWaypoint(
"EGRESS",
FlightWaypointType.EGRESS,
position,
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}",
)
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}")
@ -357,17 +342,6 @@ class WaypointBuilder:
waypoint.only_for_player = True waypoint.only_for_player = True
return waypoint return waypoint
def cas(self, position: Point) -> FlightWaypoint:
return FlightWaypoint(
"CAS",
FlightWaypointType.CAS,
position,
meters(60) if self.is_helo else meters(1000),
"RADIO",
description="Provide CAS",
pretty_name="CAS",
)
@staticmethod @staticmethod
def race_track_start(position: Point, altitude: Distance) -> FlightWaypoint: def race_track_start(position: Point, altitude: Distance) -> FlightWaypoint:
"""Creates a racetrack start waypoint. """Creates a racetrack start waypoint.

View File

@ -25,7 +25,7 @@ class FlightWaypointType(IntEnum):
INGRESS_STRIKE = 5 # Ingress strike (For generator, means that this should have bombing on next TARGET_POINT points) INGRESS_STRIKE = 5 # Ingress strike (For generator, means that this should have bombing on next TARGET_POINT points)
INGRESS_SEAD = 6 # Ingress sead (For generator, means that this should attack groups on TARGET_GROUP_LOC points) INGRESS_SEAD = 6 # Ingress sead (For generator, means that this should attack groups on TARGET_GROUP_LOC points)
INGRESS_CAS = 7 # Ingress cas (should start CAS task) INGRESS_CAS = 7 # Ingress cas (should start CAS task)
CAS = 8 # Should do CAS there CAS = 8 # Unused.
EGRESS = 9 # Should stop attack EGRESS = 9 # Should stop attack
DESCENT_POINT = 10 # Should start descending to pattern alt DESCENT_POINT = 10 # Should start descending to pattern alt
LANDING_POINT = 11 # Should land there LANDING_POINT = 11 # Should land there

View File

@ -9,7 +9,7 @@ from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.flightplan import JoinZoneGeometry from game.flightplan import JoinZoneGeometry
from game.flightplan.ipsolver import IpSolver from game.flightplan.ipsolver import IpSolver
from game.flightplan.refuelzonegeometry import RefuelZoneGeometry from game.flightplan.refuelzonegeometry import RefuelZoneGeometry
from game.persistence.paths import liberation_user_dir from game.persistence.paths import waypoint_debug_directory
from game.utils import dcs_to_shapely_point from game.utils import dcs_to_shapely_point
if TYPE_CHECKING: if TYPE_CHECKING:
@ -30,8 +30,6 @@ class PackageWaypoints:
) -> PackageWaypoints: ) -> PackageWaypoints:
origin = package.departure_closest_to_target() origin = package.departure_closest_to_target()
waypoint_debug_directory = liberation_user_dir() / "Debug/Waypoints"
# Start by picking the best IP for the attack. # Start by picking the best IP for the attack.
ip_solver = IpSolver( ip_solver = IpSolver(
dcs_to_shapely_point(origin.position), dcs_to_shapely_point(origin.position),
@ -40,7 +38,7 @@ class PackageWaypoints:
coalition.opponent.threat_zone.all, coalition.opponent.threat_zone.all,
) )
ip_solver.set_debug_properties( ip_solver.set_debug_properties(
waypoint_debug_directory / "IP", coalition.game.theater.terrain waypoint_debug_directory() / "IP", coalition.game.theater.terrain
) )
ingress_point_shapely = ip_solver.solve() ingress_point_shapely = ip_solver.solve()
if dump_debug_info: if dump_debug_info:

View File

@ -11,9 +11,13 @@ from .pydcswaypointbuilder import PydcsWaypointBuilder
class CasIngressBuilder(PydcsWaypointBuilder): class CasIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None: def add_tasks(self, waypoint: MovingPoint) -> None:
if isinstance(self.flight.flight_plan, CasFlightPlan): if isinstance(self.flight.flight_plan, CasFlightPlan):
patrol_center = (
self.flight.flight_plan.layout.patrol_start.position
+ self.flight.flight_plan.layout.patrol_end.position
) / 2
waypoint.add_task( waypoint.add_task(
EngageTargetsInZone( EngageTargetsInZone(
position=self.flight.flight_plan.layout.target.position, position=patrol_center,
radius=int(self.flight.flight_plan.engagement_distance.meters), radius=int(self.flight.flight_plan.engagement_distance.meters),
targets=[ targets=[
Targets.All.GroundUnits.GroundVehicles, Targets.All.GroundUnits.GroundVehicles,

View File

@ -29,3 +29,7 @@ def save_dir() -> Path:
def mission_path_for(name: str) -> Path: def mission_path_for(name: str) -> Path:
return Path(base_path()) / "Missions" / name return Path(base_path()) / "Missions" / name
def waypoint_debug_directory() -> Path:
return liberation_user_dir() / "Debug/Waypoints"