Plan waypoint TOTs.

Also fixes the CAP racetracks so the AI actually stays on station.

Waypoint TOT assignment happens at mission generation time for the
sake of the UI. It's a bit messy since we have the late-initialized
field in FlightWaypoint, but on the other hand we don't have to reset
every extant waypoint whenever the player adjusts the mission's TOT.

If we want to clean this up a bit more, we could have two distinct
types for waypoints: one for the planning stage and one with the
resolved TOTs. We already do some thing like this with Flight vs
FlightData.

Future improvements:

* Estimate the group's ground speed so we don't need such wide margins
  of error.
* Delay takeoff to cut loiter fuel cost.
* Plan mission TOT based on the aircraft in the package and their
  travel times to the objective.
* Tune target area time prediction. Flights often don't need to travel
  all the way to the target point, and probably won't be doing it
  slowly, so the current planning causes a lot of extra time spent in
  enemy territory.
* Per-flight TOT offsets from the package to allow a sweep to arrive
  before the rest, etc.
This commit is contained in:
Dan Albert
2020-10-05 23:07:37 -07:00
parent 7abe32be5c
commit b5e5a3b2da
12 changed files with 782 additions and 226 deletions

View File

@@ -8,14 +8,15 @@ example, the package to strike an enemy airfield may contain an escort flight,
a SEAD flight, and the strike aircraft themselves. CAP packages may contain only
the single CAP flight.
"""
import logging
from collections import defaultdict
from dataclasses import dataclass, field
import logging
from typing import Dict, Iterator, List, Optional
from typing import Dict, List, Optional
from dcs.mapping import Point
from .flights.flight import Flight, FlightType
from theater.missiontarget import MissionTarget
from .flights.flight import Flight, FlightType
@dataclass(frozen=True)
@@ -29,6 +30,14 @@ class Task:
location: str
@dataclass(frozen=True)
class PackageWaypoints:
join: Point
ingress: Point
egress: Point
split: Point
@dataclass
class Package:
"""A mission package."""
@@ -42,10 +51,10 @@ class Package:
delay: int = field(default=0)
join_point: Optional[Point] = field(default=None, init=False, hash=False)
split_point: Optional[Point] = field(default=None, init=False, hash=False)
ingress_point: Optional[Point] = field(default=None, init=False, hash=False)
egress_point: Optional[Point] = field(default=None, init=False, hash=False)
#: Desired TOT measured in seconds from mission start.
time_over_target: Optional[int] = field(default=None)
waypoints: Optional[PackageWaypoints] = field(default=None)
def add_flight(self, flight: Flight) -> None:
"""Adds a flight to the package."""
@@ -55,8 +64,7 @@ class Package:
"""Removes a flight from the package."""
self.flights.remove(flight)
if not self.flights:
self.ingress_point = None
self.egress_point = None
self.waypoints = None
@property
def primary_task(self) -> Optional[FlightType]: