Estimate TOTs for packages.

We estimate the longest possible time from mission start to TOT for
all flights in a package and use that to set the TOT (plus any delay
used to stagger flights). This both cuts down on loiter time for
shorter flights and ensures that long flights will make it to the
target in time.

This is also used to compute the start time for the AI, so the
explicit delay option is no longer needed.
This commit is contained in:
Dan Albert
2020-10-09 21:25:56 -07:00
parent d414c00b74
commit 974b6590d8
14 changed files with 497 additions and 322 deletions

View File

@@ -1,7 +1,6 @@
"""Qt data models for game objects."""
import datetime
from enum import auto, IntEnum
from typing import Any, Callable, Dict, Iterator, TypeVar, Optional
from typing import Any, Callable, Dict, Iterator, Optional, TypeVar
from PySide2.QtCore import (
QAbstractListModel,
@@ -15,6 +14,7 @@ from game import db
from game.game import Game
from gen.ato import AirTaskingOrder, Package
from gen.flights.flight import Flight
from gen.flights.traveltime import TotEstimator
from qt_ui.uiconstants import AIRCRAFT_ICONS
from theater.missiontarget import MissionTarget
@@ -119,15 +119,15 @@ class PackageModel(QAbstractListModel):
return flight
return None
@staticmethod
def text_for_flight(flight: Flight) -> str:
def text_for_flight(self, flight: Flight) -> str:
"""Returns the text that should be displayed for the flight."""
task = flight.flight_type.name
count = flight.count
name = db.unit_type_name(flight.unit_type)
delay = flight.scheduled_in
estimator = TotEstimator(self.package)
delay = datetime.timedelta(seconds=estimator.mission_start_time(flight))
origin = flight.from_cp.name
return f"[{task}] {count} x {name} from {origin} in {delay} minutes"
return f"[{task}] {count} x {name} from {origin} in {delay}"
@staticmethod
def icon_for_flight(flight: Flight) -> Optional[QIcon]:

View File

@@ -147,6 +147,8 @@ class QTopPanel(QFrame):
if not self.ato_has_clients() and not self.confirm_no_client_launch():
return
# TODO: Verify no negative start times.
# TODO: Refactor this nonsense.
game_event = None
for event in self.game.events:

View File

@@ -24,6 +24,7 @@ from PySide2.QtWidgets import (
from game import db
from gen.ato import Package
from gen.flights.flight import Flight
from gen.flights.traveltime import TotEstimator
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from ..models import AtoModel, GameModel, NullListModel, PackageModel
@@ -33,6 +34,10 @@ class FlightDelegate(QStyledItemDelegate):
HMARGIN = 4
VMARGIN = 4
def __init__(self, package: Package) -> None:
super().__init__()
self.package = package
def get_font(self, option: QStyleOptionViewItem) -> QFont:
font = QFont(option.font)
font.setPointSize(self.FONT_SIZE)
@@ -47,8 +52,9 @@ class FlightDelegate(QStyledItemDelegate):
task = flight.flight_type.name
count = flight.count
name = db.unit_type_name(flight.unit_type)
delay = flight.scheduled_in
return f"[{task}] {count} x {name} in {delay} minutes"
estimator = TotEstimator(self.package)
delay = datetime.timedelta(seconds=estimator.mission_start_time(flight))
return f"[{task}] {count} x {name} in {delay}"
def second_row_text(self, index: QModelIndex) -> str:
flight = self.flight(index)
@@ -128,7 +134,8 @@ class QFlightList(QListView):
super().__init__()
self.package_model = model
self.set_package(model)
self.setItemDelegate(FlightDelegate())
if model is not None:
self.setItemDelegate(FlightDelegate(model.package))
self.setIconSize(QSize(91, 24))
self.setSelectionBehavior(QAbstractItemView.SelectItems)
@@ -138,6 +145,7 @@ class QFlightList(QListView):
self.disconnect_model()
else:
self.package_model = model
self.setItemDelegate(FlightDelegate(model.package))
self.setModel(model)
# noinspection PyUnresolvedReferences
model.deleted.connect(self.disconnect_model)

View File

@@ -21,7 +21,8 @@ from game import Game, db
from game.data.aaa_db import AAA_UNITS
from game.data.radar_db import UNITS_WITH_RADAR
from game.utils import meter_to_feet
from gen import Conflict, Package, PackageWaypointTiming
from gen import Conflict, PackageWaypointTiming
from gen.ato import Package
from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType
from qt_ui.displayoptions import DisplayOptions
from qt_ui.models import GameModel

View File

@@ -1,26 +1,28 @@
import datetime
from PySide2.QtGui import QStandardItem, QIcon
from game import db
from gen.ato import Package
from gen.flights.flight import Flight
from gen.flights.traveltime import TotEstimator
from qt_ui.uiconstants import AIRCRAFT_ICONS
# TODO: Replace with QFlightList.
class QFlightItem(QStandardItem):
def __init__(self, flight:Flight):
def __init__(self, package: Package, flight: Flight):
super(QFlightItem, self).__init__()
self.package = package
self.flight = flight
if db.unit_type_name(self.flight.unit_type).replace("/", " ") in AIRCRAFT_ICONS.keys():
icon = QIcon((AIRCRAFT_ICONS[db.unit_type_name(self.flight.unit_type)]))
self.setIcon(icon)
self.setEditable(False)
estimator = TotEstimator(self.package)
delay = datetime.timedelta(seconds=estimator.mission_start_time(flight))
self.setText("["+str(self.flight.flight_type.name[:6])+"] "
+ str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type)
+ " in " + str(self.flight.scheduled_in) + " minutes")
def update(self, flight):
self.flight = flight
self.setText("[" + str(self.flight.flight_type.name[:6]) + "] "
+ str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type)
+ " in " + str(self.flight.scheduled_in) + " minutes")
+ " in " + str(delay))

View File

@@ -116,11 +116,14 @@ class QPackageDialog(QDialog):
self.finished.connect(self.on_close)
def on_close(self, _result) -> None:
@staticmethod
def on_close(_result) -> None:
GameUpdateSignal.get_instance().redraw_flight_paths()
def save_tot(self) -> None:
time = self.tot_spinner.time()
seconds = time.hour() * 3600 + time.minute() * 60 + time.second()
self.package_model.update_tot(seconds)
GameUpdateSignal.get_instance().redraw_flight_paths()
def on_selection_changed(self, selected: QItemSelection,
_deselected: QItemSelection) -> None:
@@ -182,6 +185,7 @@ class QNewPackageDialog(QPackageDialog):
Empty packages may be created. They can be modified later, and will have
no effect if empty when the mission is generated.
"""
self.save_tot()
self.ato_model.add_package(self.package_model.package)
for flight in self.package_model.package.flights:
self.game.aircraft_inventory.claim_for_flight(flight)
@@ -227,6 +231,7 @@ class QEditPackageDialog(QPackageDialog):
def on_done(self) -> None:
"""Closes the window."""
self.save_tot()
self.close()
def on_delete(self) -> None:

View File

@@ -90,7 +90,11 @@ class QFlightCreator(QDialog):
origin = self.airfield_selector.currentData()
size = self.flight_size_spinner.value()
flight = Flight(aircraft, size, origin, task)
if self.game.settings.perf_ai_parking_start:
start_type = "Cold"
else:
start_type = "Warm"
flight = Flight(aircraft, size, origin, task, start_type)
flight.scheduled_in = self.package.delay
# noinspection PyUnresolvedReferences

View File

@@ -1,6 +1,7 @@
from PySide2.QtWidgets import QLabel, QHBoxLayout, QGroupBox, QSpinBox
# TODO: Remove?
class QFlightDepartureEditor(QGroupBox):
def __init__(self, flight):
@@ -15,7 +16,7 @@ class QFlightDepartureEditor(QGroupBox):
self.departure_delta = QSpinBox(self)
self.departure_delta.setMinimum(0)
self.departure_delta.setMaximum(120)
self.departure_delta.setValue(self.flight.scheduled_in)
self.departure_delta.setValue(self.flight.scheduled_in // 60)
self.departure_delta.valueChanged.connect(self.change_scheduled)
layout.addWidget(self.depart_from)
@@ -27,4 +28,4 @@ class QFlightDepartureEditor(QGroupBox):
self.changed = self.departure_delta.valueChanged
def change_scheduled(self):
self.flight.scheduled_in = int(self.departure_delta.value())
self.flight.scheduled_in = int(self.departure_delta.value() * 60)