mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Clean some cruft out of FlightPlanBuilder.
This commit is contained in:
parent
6bbe583e82
commit
9cd511da3d
@ -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")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user