From cb3bf56d84bb40f585200cf412d0e28647be07ec Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Thu, 10 Aug 2023 00:03:30 -0700 Subject: [PATCH] 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. --- changelog.md | 1 + game/ato/flightplans/aewc.py | 2 +- game/ato/flightplans/airassault.py | 2 +- game/ato/flightplans/airlift.py | 2 +- game/ato/flightplans/antiship.py | 2 +- game/ato/flightplans/bai.py | 2 +- game/ato/flightplans/barcap.py | 2 +- game/ato/flightplans/cas.py | 94 +++++++++++++------ game/ato/flightplans/custom.py | 2 +- game/ato/flightplans/dead.py | 2 +- game/ato/flightplans/escort.py | 2 +- game/ato/flightplans/ferry.py | 2 +- game/ato/flightplans/ibuilder.py | 8 +- game/ato/flightplans/ocaaircraft.py | 2 +- game/ato/flightplans/ocarunway.py | 2 +- game/ato/flightplans/packagerefueling.py | 2 +- game/ato/flightplans/rtb.py | 2 +- game/ato/flightplans/sead.py | 2 +- game/ato/flightplans/shiprecoverytanker.py | 2 +- game/ato/flightplans/strike.py | 2 +- game/ato/flightplans/sweep.py | 2 +- game/ato/flightplans/tarcap.py | 2 +- game/ato/flightplans/theaterrefueling.py | 2 +- game/ato/flightplans/waypointbuilder.py | 26 ----- game/ato/flightwaypointtype.py | 2 +- game/ato/packagewaypoints.py | 6 +- .../aircraft/waypoints/casingress.py | 6 +- game/persistence/paths.py | 4 + 28 files changed, 100 insertions(+), 87 deletions(-) diff --git a/changelog.md b/changelog.md index bf490711..1c2be905 100644 --- a/changelog.md +++ b/changelog.md @@ -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]** 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]** 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]** 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+. diff --git a/game/ato/flightplans/aewc.py b/game/ato/flightplans/aewc.py index 6da848e5..7534ce32 100644 --- a/game/ato/flightplans/aewc.py +++ b/game/ato/flightplans/aewc.py @@ -90,5 +90,5 @@ class Builder(IBuilder[AewcFlightPlan, PatrollingLayout]): bullseye=builder.bullseye(), ) - def build(self) -> AewcFlightPlan: + def build(self, dump_debug_info: bool = False) -> AewcFlightPlan: return AewcFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/airassault.py b/game/ato/flightplans/airassault.py index 721323a2..ac37640b 100644 --- a/game/ato/flightplans/airassault.py +++ b/game/ato/flightplans/airassault.py @@ -152,5 +152,5 @@ class Builder(IBuilder[AirAssaultFlightPlan, AirAssaultLayout]): bullseye=builder.bullseye(), ) - def build(self) -> AirAssaultFlightPlan: + def build(self, dump_debug_info: bool = False) -> AirAssaultFlightPlan: return AirAssaultFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/airlift.py b/game/ato/flightplans/airlift.py index 39139b41..d62c4e7a 100644 --- a/game/ato/flightplans/airlift.py +++ b/game/ato/flightplans/airlift.py @@ -155,5 +155,5 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]): bullseye=builder.bullseye(), ) - def build(self) -> AirliftFlightPlan: + def build(self, dump_debug_info: bool = False) -> AirliftFlightPlan: return AirliftFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/antiship.py b/game/ato/flightplans/antiship.py index 59d4f89f..b883fd9f 100644 --- a/game/ato/flightplans/antiship.py +++ b/game/ato/flightplans/antiship.py @@ -41,5 +41,5 @@ class Builder(FormationAttackBuilder[AntiShipFlightPlan, FormationAttackLayout]) 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] - def build(self) -> AntiShipFlightPlan: + def build(self, dump_debug_info: bool = False) -> AntiShipFlightPlan: return AntiShipFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/bai.py b/game/ato/flightplans/bai.py index e64e27ac..22b8f915 100644 --- a/game/ato/flightplans/bai.py +++ b/game/ato/flightplans/bai.py @@ -39,5 +39,5 @@ class Builder(FormationAttackBuilder[BaiFlightPlan, FormationAttackLayout]): 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()) diff --git a/game/ato/flightplans/barcap.py b/game/ato/flightplans/barcap.py index 615dcf48..43c5535e 100644 --- a/game/ato/flightplans/barcap.py +++ b/game/ato/flightplans/barcap.py @@ -66,5 +66,5 @@ class Builder(CapBuilder[BarCapFlightPlan, PatrollingLayout]): bullseye=builder.bullseye(), ) - def build(self) -> BarCapFlightPlan: + def build(self, dump_debug_info: bool = False) -> BarCapFlightPlan: return BarCapFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/cas.py b/game/ato/flightplans/cas.py index 75b367f4..5a379ced 100644 --- a/game/ato/flightplans/cas.py +++ b/game/ato/flightplans/cas.py @@ -6,13 +6,15 @@ from datetime import timedelta from typing import TYPE_CHECKING, Type 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 .invalidobjectivelocation import InvalidObjectiveLocation from .patrolling import PatrollingFlightPlan, PatrollingLayout from .uizonedisplay import UiZone, UiZoneDisplay from .waypointbuilder import WaypointBuilder from ..flightwaypointtype import FlightWaypointType +from ...flightplan.ipsolver import IpSolver +from ...persistence.paths import waypoint_debug_directory if TYPE_CHECKING: from ..flightwaypoint import FlightWaypoint @@ -20,13 +22,13 @@ if TYPE_CHECKING: @dataclass(frozen=True) class CasLayout(PatrollingLayout): - target: FlightWaypoint + ingress: FlightWaypoint def iter_waypoints(self) -> Iterator[FlightWaypoint]: yield self.departure yield from self.nav_to + yield self.ingress yield self.patrol_start - yield self.target yield self.patrol_end yield from self.nav_from yield self.arrival @@ -59,7 +61,7 @@ class CasFlightPlan(PatrollingFlightPlan[CasLayout], UiZoneDisplay): @property 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: return self.layout.patrol_start @@ -68,14 +70,17 @@ class CasFlightPlan(PatrollingFlightPlan[CasLayout], UiZoneDisplay): return self.layout.patrol_end def ui_zone(self) -> UiZone: + midpoint = ( + self.layout.patrol_start.position + self.layout.patrol_end.position + ) / 2 return UiZone( - [self.layout.target.position], + [midpoint], self.engagement_distance, ) class Builder(IBuilder[CasFlightPlan, CasLayout]): - def layout(self) -> CasLayout: + def layout(self, dump_debug_info: bool) -> CasLayout: location = self.package.target if not isinstance(location, FrontLine): @@ -86,46 +91,77 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]): ) bounds = FrontLineConflictDescription.frontline_bounds(location, self.theater) - ingress = bounds.left_position - center = bounds.center - egress = bounds.right_position + patrol_start = bounds.left_position + patrol_end = bounds.right_position - ingress_distance = ingress.distance_to_point(self.flight.departure.position) - egress_distance = egress.distance_to_point(self.flight.departure.position) - if egress_distance < ingress_distance: - ingress, egress = egress, ingress + start_distance = patrol_start.distance_to_point(self.flight.departure.position) + end_distance = patrol_end.distance_to_point(self.flight.departure.position) + if end_distance < start_distance: + patrol_start, patrol_end = patrol_end, patrol_start builder = WaypointBuilder(self.flight, self.coalition) is_helo = self.flight.unit_type.dcs_unit_type.helicopter - ingress_egress_altitude = ( - self.doctrine.ingress_altitude if not is_helo else meters(50) + patrol_altitude = 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( departure=builder.takeoff(self.flight.departure), nav_to=builder.nav_path( self.flight.departure.position, - ingress, - ingress_egress_altitude, - use_agl_ingress_egress, + ingress_point, + patrol_altitude, + use_agl_patrol_altitude, ), nav_from=builder.nav_path( - egress, + patrol_end, self.flight.arrival.position, - ingress_egress_altitude, - use_agl_ingress_egress, + patrol_altitude, + use_agl_patrol_altitude, ), - patrol_start=builder.ingress( - FlightWaypointType.INGRESS_CAS, ingress, location - ), - target=builder.cas(center), - patrol_end=builder.egress(egress, location), + ingress=ingress, + patrol_start=patrol_start_waypoint, + patrol_end=patrol_end_waypoint, arrival=builder.land(self.flight.arrival), divert=builder.divert(self.flight.divert), bullseye=builder.bullseye(), ) - def build(self) -> CasFlightPlan: - return CasFlightPlan(self.flight, self.layout()) + def build(self, dump_debug_info: bool = False) -> CasFlightPlan: + return CasFlightPlan(self.flight, self.layout(dump_debug_info)) diff --git a/game/ato/flightplans/custom.py b/game/ato/flightplans/custom.py index 19c0b559..abb3454f 100644 --- a/game/ato/flightplans/custom.py +++ b/game/ato/flightplans/custom.py @@ -72,5 +72,5 @@ class Builder(IBuilder[CustomFlightPlan, CustomLayout]): builder = WaypointBuilder(self.flight, self.coalition) 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()) diff --git a/game/ato/flightplans/dead.py b/game/ato/flightplans/dead.py index 6827130d..7f240ced 100644 --- a/game/ato/flightplans/dead.py +++ b/game/ato/flightplans/dead.py @@ -37,5 +37,5 @@ class Builder(FormationAttackBuilder[DeadFlightPlan, FormationAttackLayout]): 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()) diff --git a/game/ato/flightplans/escort.py b/game/ato/flightplans/escort.py index a176fd40..5e1ca54d 100644 --- a/game/ato/flightplans/escort.py +++ b/game/ato/flightplans/escort.py @@ -50,5 +50,5 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]): bullseye=builder.bullseye(), ) - def build(self) -> EscortFlightPlan: + def build(self, dump_debug_info: bool = False) -> EscortFlightPlan: return EscortFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/ferry.py b/game/ato/flightplans/ferry.py index 2a6fa3a0..e7c2f3b3 100644 --- a/game/ato/flightplans/ferry.py +++ b/game/ato/flightplans/ferry.py @@ -83,5 +83,5 @@ class Builder(IBuilder[FerryFlightPlan, FerryLayout]): bullseye=builder.bullseye(), ) - def build(self) -> FerryFlightPlan: + def build(self, dump_debug_info: bool = False) -> FerryFlightPlan: return FerryFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/ibuilder.py b/game/ato/flightplans/ibuilder.py index b85589e4..a91cd6f3 100644 --- a/game/ato/flightplans/ibuilder.py +++ b/game/ato/flightplans/ibuilder.py @@ -35,7 +35,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]): def regenerate(self, dump_debug_info: bool = False) -> None: try: 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: color = "blue" if self.flight.squadron.player else "red" raise PlanningError( @@ -59,11 +59,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]): return self.flight.departure.theater @abstractmethod - def layout(self) -> LayoutT: - ... - - @abstractmethod - def build(self) -> FlightPlanT: + def build(self, dump_debug_info: bool = False) -> FlightPlanT: ... @property diff --git a/game/ato/flightplans/ocaaircraft.py b/game/ato/flightplans/ocaaircraft.py index 5b8ba6da..61cffce1 100644 --- a/game/ato/flightplans/ocaaircraft.py +++ b/game/ato/flightplans/ocaaircraft.py @@ -32,5 +32,5 @@ class Builder(FormationAttackBuilder[OcaAircraftFlightPlan, FormationAttackLayou 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()) diff --git a/game/ato/flightplans/ocarunway.py b/game/ato/flightplans/ocarunway.py index fd7b3bfd..234d8f41 100644 --- a/game/ato/flightplans/ocarunway.py +++ b/game/ato/flightplans/ocarunway.py @@ -32,5 +32,5 @@ class Builder(FormationAttackBuilder[OcaRunwayFlightPlan, FormationAttackLayout] 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()) diff --git a/game/ato/flightplans/packagerefueling.py b/game/ato/flightplans/packagerefueling.py index cec75cd8..0e0b4d86 100644 --- a/game/ato/flightplans/packagerefueling.py +++ b/game/ato/flightplans/packagerefueling.py @@ -121,5 +121,5 @@ class Builder(IBuilder[PackageRefuelingFlightPlan, PatrollingLayout]): bullseye=builder.bullseye(), ) - def build(self) -> PackageRefuelingFlightPlan: + def build(self, dump_debug_info: bool = False) -> PackageRefuelingFlightPlan: return PackageRefuelingFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/rtb.py b/game/ato/flightplans/rtb.py index 56e87268..34fb0206 100644 --- a/game/ato/flightplans/rtb.py +++ b/game/ato/flightplans/rtb.py @@ -93,5 +93,5 @@ class Builder(IBuilder[RtbFlightPlan, RtbLayout]): bullseye=builder.bullseye(), ) - def build(self) -> RtbFlightPlan: + def build(self, dump_debug_info: bool = False) -> RtbFlightPlan: return RtbFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/sead.py b/game/ato/flightplans/sead.py index 3c6e89ae..2a691026 100644 --- a/game/ato/flightplans/sead.py +++ b/game/ato/flightplans/sead.py @@ -24,5 +24,5 @@ class Builder(FormationAttackBuilder[SeadFlightPlan, FormationAttackLayout]): def layout(self) -> FormationAttackLayout: 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()) diff --git a/game/ato/flightplans/shiprecoverytanker.py b/game/ato/flightplans/shiprecoverytanker.py index cc758eff..22746989 100644 --- a/game/ato/flightplans/shiprecoverytanker.py +++ b/game/ato/flightplans/shiprecoverytanker.py @@ -91,5 +91,5 @@ class Builder(IBuilder[RecoveryTankerFlightPlan, RecoveryTankerLayout]): bullseye=builder.bullseye(), ) - def build(self) -> RecoveryTankerFlightPlan: + def build(self, dump_debug_info: bool = False) -> RecoveryTankerFlightPlan: return RecoveryTankerFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/strike.py b/game/ato/flightplans/strike.py index 303c50b6..e04387c3 100644 --- a/game/ato/flightplans/strike.py +++ b/game/ato/flightplans/strike.py @@ -32,5 +32,5 @@ class Builder(FormationAttackBuilder[StrikeFlightPlan, FormationAttackLayout]): 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()) diff --git a/game/ato/flightplans/sweep.py b/game/ato/flightplans/sweep.py index 2161e8d5..aba95c2d 100644 --- a/game/ato/flightplans/sweep.py +++ b/game/ato/flightplans/sweep.py @@ -137,5 +137,5 @@ class Builder(IBuilder[SweepFlightPlan, SweepLayout]): target, origin, ip, join, self.coalition, self.theater ).find_best_hold_point() - def build(self) -> SweepFlightPlan: + def build(self, dump_debug_info: bool = False) -> SweepFlightPlan: return SweepFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/tarcap.py b/game/ato/flightplans/tarcap.py index 7542dffe..3f6d4ac4 100644 --- a/game/ato/flightplans/tarcap.py +++ b/game/ato/flightplans/tarcap.py @@ -122,5 +122,5 @@ class Builder(CapBuilder[TarCapFlightPlan, TarCapLayout]): bullseye=builder.bullseye(), ) - def build(self) -> TarCapFlightPlan: + def build(self, dump_debug_info: bool = False) -> TarCapFlightPlan: return TarCapFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/theaterrefueling.py b/game/ato/flightplans/theaterrefueling.py index 08ed94eb..d93eb4a8 100644 --- a/game/ato/flightplans/theaterrefueling.py +++ b/game/ato/flightplans/theaterrefueling.py @@ -79,5 +79,5 @@ class Builder(IBuilder[TheaterRefuelingFlightPlan, PatrollingLayout]): bullseye=builder.bullseye(), ) - def build(self) -> TheaterRefuelingFlightPlan: + def build(self, dump_debug_info: bool = False) -> TheaterRefuelingFlightPlan: return TheaterRefuelingFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/waypointbuilder.py b/game/ato/flightplans/waypointbuilder.py index e9cd233e..8cb75cdd 100644 --- a/game/ato/flightplans/waypointbuilder.py +++ b/game/ato/flightplans/waypointbuilder.py @@ -253,21 +253,6 @@ class WaypointBuilder: 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: return self._target_point(target, f"ATTACK {target.name}") @@ -357,17 +342,6 @@ class WaypointBuilder: waypoint.only_for_player = True 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 def race_track_start(position: Point, altitude: Distance) -> FlightWaypoint: """Creates a racetrack start waypoint. diff --git a/game/ato/flightwaypointtype.py b/game/ato/flightwaypointtype.py index 5c489746..ab79fd5c 100644 --- a/game/ato/flightwaypointtype.py +++ b/game/ato/flightwaypointtype.py @@ -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_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) - CAS = 8 # Should do CAS there + CAS = 8 # Unused. EGRESS = 9 # Should stop attack DESCENT_POINT = 10 # Should start descending to pattern alt LANDING_POINT = 11 # Should land there diff --git a/game/ato/packagewaypoints.py b/game/ato/packagewaypoints.py index d53bc558..c0450576 100644 --- a/game/ato/packagewaypoints.py +++ b/game/ato/packagewaypoints.py @@ -9,7 +9,7 @@ from game.ato.flightplans.waypointbuilder import WaypointBuilder from game.flightplan import JoinZoneGeometry from game.flightplan.ipsolver import IpSolver 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 if TYPE_CHECKING: @@ -30,8 +30,6 @@ class PackageWaypoints: ) -> PackageWaypoints: origin = package.departure_closest_to_target() - waypoint_debug_directory = liberation_user_dir() / "Debug/Waypoints" - # Start by picking the best IP for the attack. ip_solver = IpSolver( dcs_to_shapely_point(origin.position), @@ -40,7 +38,7 @@ class PackageWaypoints: coalition.opponent.threat_zone.all, ) 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() if dump_debug_info: diff --git a/game/missiongenerator/aircraft/waypoints/casingress.py b/game/missiongenerator/aircraft/waypoints/casingress.py index d63b20dc..fc39b8c8 100644 --- a/game/missiongenerator/aircraft/waypoints/casingress.py +++ b/game/missiongenerator/aircraft/waypoints/casingress.py @@ -11,9 +11,13 @@ from .pydcswaypointbuilder import PydcsWaypointBuilder class CasIngressBuilder(PydcsWaypointBuilder): def add_tasks(self, waypoint: MovingPoint) -> None: 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( EngageTargetsInZone( - position=self.flight.flight_plan.layout.target.position, + position=patrol_center, radius=int(self.flight.flight_plan.engagement_distance.meters), targets=[ Targets.All.GroundUnits.GroundVehicles, diff --git a/game/persistence/paths.py b/game/persistence/paths.py index d32cfb44..6133e457 100644 --- a/game/persistence/paths.py +++ b/game/persistence/paths.py @@ -29,3 +29,7 @@ def save_dir() -> Path: def mission_path_for(name: str) -> Path: return Path(base_path()) / "Missions" / name + + +def waypoint_debug_directory() -> Path: + return liberation_user_dir() / "Debug/Waypoints"