Clean some cruft out of FlightPlanBuilder.

This commit is contained in:
Dan Albert 2022-08-21 18:07:10 -07:00
parent 6bbe583e82
commit 9cd511da3d
4 changed files with 75 additions and 96 deletions

View File

@ -3,10 +3,6 @@ from __future__ import annotations
from typing import Any, TYPE_CHECKING, Type
from game.ato import FlightType
from game.ato.closestairfields import ObjectiveDistanceCache
from game.data.doctrine import Doctrine
from game.flightplan import IpZoneGeometry, JoinZoneGeometry
from game.flightplan.refuelzonegeometry import RefuelZoneGeometry
from .aewc import AewcFlightPlan
from .airassault import AirAssaultFlightPlan
from .airlift import AirliftFlightPlan
@ -27,13 +23,12 @@ from .strike import StrikeFlightPlan
from .sweep import SweepFlightPlan
from .tarcap import TarCapFlightPlan
from .theaterrefueling import TheaterRefuelingFlightPlan
from .waypointbuilder import WaypointBuilder
from ..packagewaypoints import PackageWaypoints
if TYPE_CHECKING:
from game.ato import Flight, FlightWaypoint, Package
from game.ato import Flight, Package
from game.coalition import Coalition
from game.theater import ConflictTheater, ControlPoint, FrontLine
from game.threatzones import ThreatZones
from game.theater import ConflictTheater, FrontLine
class FlightPlanBuilder:
@ -57,14 +52,6 @@ class FlightPlanBuilder:
def is_player(self) -> bool:
return self.coalition.player
@property
def doctrine(self) -> Doctrine:
return self.coalition.doctrine
@property
def threat_zones(self) -> ThreatZones:
return self.coalition.opponent.threat_zone
def populate_flight_plan(self, flight: Flight) -> None:
"""Creates a default flight plan for the given mission."""
if flight not in self.package.flights:
@ -74,7 +61,9 @@ class FlightPlanBuilder:
try:
if self.package.waypoints is None:
self.regenerate_package_waypoints()
self.package.waypoints = PackageWaypoints.create(
self.package, self.coalition
)
flight.flight_plan = self.generate_flight_plan(flight)
except NavMeshError as ex:
color = "blue" if self.is_player else "red"
@ -121,76 +110,3 @@ class FlightPlanBuilder:
)
layout = plan_type.builder_type()(flight, self.theater).build()
return plan_type(flight, layout)
def regenerate_flight_plans(self) -> None:
new_flights: list[Flight] = []
for old_flight in self.package.flights:
old_flight.flight_plan = self.generate_flight_plan(old_flight)
new_flights.append(old_flight)
self.package.flights = new_flights
def regenerate_package_waypoints(self) -> None:
from game.ato.packagewaypoints import PackageWaypoints
package_airfield = self.package_airfield()
# Start by picking the best IP for the attack.
ingress_point = IpZoneGeometry(
self.package.target.position,
package_airfield.position,
self.coalition,
).find_best_ip()
join_point = JoinZoneGeometry(
self.package.target.position,
package_airfield.position,
ingress_point,
self.coalition,
).find_best_join_point()
refuel_point = RefuelZoneGeometry(
package_airfield.position,
join_point,
self.coalition,
).find_best_refuel_point()
# And the split point based on the best route from the IP. Since that's no
# different than the best route *to* the IP, this is the same as the join point.
# TODO: Estimate attack completion point based on the IP and split from there?
self.package.waypoints = PackageWaypoints(
WaypointBuilder.perturb(join_point),
ingress_point,
WaypointBuilder.perturb(join_point),
refuel_point,
)
# TODO: Make a model for the waypoint builder and use that in the UI.
def generate_rtb_waypoint(
self, flight: Flight, arrival: ControlPoint
) -> FlightWaypoint:
"""Generate RTB landing point.
Args:
flight: The flight to generate the landing waypoint for.
arrival: Arrival airfield or carrier.
"""
builder = WaypointBuilder(flight, self.coalition)
return builder.land(arrival)
def package_airfield(self) -> ControlPoint:
# We'll always have a package, but if this is being planned via the UI
# it could be the first flight in the package.
if not self.package.flights:
raise PlanningError(
"Cannot determine source airfield for package with no flights"
)
# The package airfield is either the flight's airfield (when there is no
# package) or the closest airfield to the objective that is the
# departure airfield for some flight in the package.
cache = ObjectiveDistanceCache.get_closest_airfields(self.package.target)
for airfield in cache.operational_airfields:
for flight in self.package.flights:
if flight.departure == airfield:
return airfield
raise PlanningError("Could not find any airfield assigned to this package")

View File

@ -6,16 +6,17 @@ from dataclasses import dataclass, field
from datetime import timedelta
from typing import Dict, List, Optional, TYPE_CHECKING
from .flightplans.formation import FormationFlightPlan
from game.db import Database
from game.utils import Speed
from .closestairfields import ObjectiveDistanceCache
from .flight import Flight
from .flightplans.formation import FormationFlightPlan
from .flighttype import FlightType
from .packagewaypoints import PackageWaypoints
from .traveltime import TotEstimator
if TYPE_CHECKING:
from game.theater import MissionTarget
from game.theater import ControlPoint, MissionTarget
@dataclass
@ -193,6 +194,24 @@ class Package:
return "OCA Strike"
return str(task)
def departure_closest_to_target(self) -> ControlPoint:
# We'll always have a package, but if this is being planned via the UI
# it could be the first flight in the package.
if not self.flights:
raise RuntimeError(
"Cannot determine source airfield for package with no flights"
)
# The package airfield is either the flight's airfield (when there is no
# package) or the closest airfield to the objective that is the
# departure airfield for some flight in the package.
cache = ObjectiveDistanceCache.get_closest_airfields(self.target)
for airfield in cache.operational_airfields:
for flight in self.flights:
if flight.departure == airfield:
return airfield
raise RuntimeError("Could not find any airfield assigned to this package")
def __hash__(self) -> int:
# TODO: Far from perfect. Number packages?
return hash(self.target.name)

View File

@ -1,8 +1,18 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
from typing import TYPE_CHECKING
from dcs import Point
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.flightplan import IpZoneGeometry, JoinZoneGeometry
from game.flightplan.refuelzonegeometry import RefuelZoneGeometry
if TYPE_CHECKING:
from game.ato import Package
from game.coalition import Coalition
@dataclass(frozen=True)
class PackageWaypoints:
@ -10,3 +20,37 @@ class PackageWaypoints:
ingress: Point
split: Point
refuel: Point
@staticmethod
def create(package: Package, coalition: Coalition) -> PackageWaypoints:
origin = package.departure_closest_to_target()
# Start by picking the best IP for the attack.
ingress_point = IpZoneGeometry(
package.target.position,
origin.position,
coalition,
).find_best_ip()
join_point = JoinZoneGeometry(
package.target.position,
origin.position,
ingress_point,
coalition,
).find_best_join_point()
refuel_point = RefuelZoneGeometry(
origin.position,
join_point,
coalition,
).find_best_refuel_point()
# And the split point based on the best route from the IP. Since that's no
# different than the best route *to* the IP, this is the same as the join point.
# TODO: Estimate attack completion point based on the IP and split from there?
return PackageWaypoints(
WaypointBuilder.perturb(join_point),
ingress_point,
WaypointBuilder.perturb(join_point),
refuel_point,
)

View File

@ -1,5 +1,5 @@
import logging
from typing import Iterable, List, Optional, Any
from typing import Iterable, List, Optional
from PySide2.QtCore import Signal
from PySide2.QtWidgets import (
@ -14,10 +14,10 @@ from PySide2.QtWidgets import (
from game import Game
from game.ato.flight import Flight
from game.ato.flightplans.custom import CustomFlightPlan, CustomLayout
from game.ato.flightplans.flightplan import FlightPlan
from game.ato.flightplans.flightplanbuilder import FlightPlanBuilder
from game.ato.flightplans.formationattack import FormationAttackFlightPlan
from game.ato.flightplans.planningerror import PlanningError
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.ato.flighttype import FlightType
from game.ato.flightwaypoint import FlightWaypoint
from game.ato.loadouts import Loadout
@ -139,7 +139,7 @@ class QFlightWaypointTab(QFrame):
self.on_change()
def on_rtb_waypoint(self):
rtb = self.planner.generate_rtb_waypoint(self.flight, self.flight.from_cp)
rtb = WaypointBuilder(self.flight, self.coalition).land(self.flight.arrival)
self.degrade_to_custom_flight_plan()
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
self.flight.flight_plan.layout.custom_waypoints.append(rtb)