Airlift support.

UI isn't finished. Bulk transfers where the player doesn't care what
aircraft get used work (though they're chosen with no thought at all),
but being able to plan your own airlift flight isn't here yet.

Cargo planes are not implemented yet.

No way to view the cargo of a flight (will come with the cargo flight
planning UI).

The airlift flight/package creation should probably be moved out of the
UI and into the game code.

AI doesn't use these yet.

https://github.com/Khopa/dcs_liberation/issues/825
This commit is contained in:
Dan Albert
2021-04-21 17:13:35 -07:00
parent 8e361a8776
commit 481f195725
12 changed files with 406 additions and 73 deletions

View File

@@ -15,7 +15,7 @@ from PySide2.QtGui import QIcon
from game import db
from game.game import Game
from game.theater.missiontarget import MissionTarget
from game.transfers import RoadTransferOrder
from game.transfers import TransferOrder
from gen.ato import AirTaskingOrder, Package
from gen.flights.flight import Flight
from gen.flights.traveltime import TotEstimator
@@ -52,8 +52,13 @@ class DeletableChildModelManager:
ModelDict = Dict[DataType, ModelType]
def __init__(self, create_model: Callable[[DataType], ModelType]) -> None:
def __init__(
self,
create_model: Callable[[DataType, GameModel], ModelType],
game_model: GameModel,
) -> None:
self.create_model = create_model
self.game_model = game_model
self.models: DeletableChildModelManager.ModelDict = {}
def acquire(self, data: DataType) -> ModelType:
@@ -64,7 +69,7 @@ class DeletableChildModelManager:
"""
if data in self.models:
return self.models[data]
model = self.create_model(data)
model = self.create_model(data, self.game_model)
self.models[data] = model
return model
@@ -105,9 +110,10 @@ class PackageModel(QAbstractListModel):
tot_changed = Signal()
def __init__(self, package: Package) -> None:
def __init__(self, package: Package, game_model: GameModel) -> None:
super().__init__()
self.package = package
self.game_model = game_model
def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
return len(self.package.flights)
@@ -154,14 +160,15 @@ class PackageModel(QAbstractListModel):
self.delete_flight(self.flight_at_index(index))
def delete_flight(self, flight: Flight) -> None:
"""Removes the given flight from the package.
If the flight is using claimed inventory, the caller is responsible for
returning that inventory.
"""
"""Removes the given flight from the package."""
index = self.package.flights.index(flight)
self.beginRemoveRows(QModelIndex(), index, index)
self.package.remove_flight(flight)
if flight.cargo is None:
self.game_model.game.aircraft_inventory.return_from_flight(flight)
self.package.remove_flight(flight)
else:
# Deleted transfers will clean up after themselves.
self.game_model.transfer_model.cancel_transfer(flight.cargo)
self.endRemoveRows()
self.update_tot()
@@ -210,11 +217,15 @@ class AtoModel(QAbstractListModel):
client_slots_changed = Signal()
def __init__(self, game: Optional[Game], ato: AirTaskingOrder) -> None:
def __init__(self, game_model: GameModel, ato: AirTaskingOrder) -> None:
super().__init__()
self.game = game
self.game_model = game_model
self.ato = ato
self.package_models = DeletableChildModelManager(PackageModel)
self.package_models = DeletableChildModelManager(PackageModel, game_model)
@property
def game(self) -> Optional[Game]:
return self.game_model.game
def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
return len(self.ato.packages)
@@ -249,6 +260,8 @@ class AtoModel(QAbstractListModel):
self.ato.remove_package(package)
for flight in package.flights:
self.game.aircraft_inventory.return_from_flight(flight)
if flight.cargo is not None:
self.game_model.transfer_model.cancel_transfer(flight.cargo)
self.endRemoveRows()
# noinspection PyUnresolvedReferences
self.client_slots_changed.emit()
@@ -257,20 +270,19 @@ class AtoModel(QAbstractListModel):
"""Returns the package at the given index."""
return self.ato.packages[index.row()]
def replace_from_game(self, game: Optional[Game], player: bool) -> None:
def replace_from_game(self, player: bool) -> None:
"""Updates the ATO object to match the updated game object.
If the game is None (as is the case when no game has been loaded), an
empty ATO will be used.
"""
self.beginResetModel()
self.game = game
self.package_models.clear()
if self.game is not None:
if player:
self.ato = game.blue_ato
self.ato = self.game.blue_ato
else:
self.ato = game.red_ato
self.ato = self.game.red_ato
else:
self.ato = AirTaskingOrder()
self.endResetModel()
@@ -313,7 +325,7 @@ class TransferModel(QAbstractListModel):
return None
@staticmethod
def text_for_transfer(transfer: RoadTransferOrder) -> str:
def text_for_transfer(transfer: TransferOrder) -> str:
"""Returns the text that should be displayed for the transfer."""
count = sum(transfer.units.values())
origin = transfer.origin.name
@@ -321,11 +333,11 @@ class TransferModel(QAbstractListModel):
return f"Transfer of {count} units from {origin} to {destination}"
@staticmethod
def icon_for_transfer(_transfer: RoadTransferOrder) -> Optional[QIcon]:
def icon_for_transfer(_transfer: TransferOrder) -> Optional[QIcon]:
"""Returns the icon that should be displayed for the transfer."""
return None
def new_transfer(self, transfer: RoadTransferOrder) -> None:
def new_transfer(self, transfer: TransferOrder) -> None:
"""Updates the game with the new unit transfer."""
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
# TODO: Needs to regenerate base inventory tab.
@@ -334,13 +346,17 @@ class TransferModel(QAbstractListModel):
def cancel_transfer_at_index(self, index: QModelIndex) -> None:
"""Cancels the planned unit transfer at the given index."""
transfer = self.transfer_at_index(index)
self.beginRemoveRows(QModelIndex(), index.row(), index.row())
self.cancel_transfer(self.transfer_at_index(index))
def cancel_transfer(self, transfer: TransferOrder) -> None:
"""Cancels the planned unit transfer at the given index."""
index = self.game_model.game.transfers.index_of_transfer(transfer)
self.beginRemoveRows(QModelIndex(), index, index)
# TODO: Needs to regenerate base inventory tab.
self.game_model.game.transfers.cancel_transfer(transfer)
self.endRemoveRows()
def transfer_at_index(self, index: QModelIndex) -> RoadTransferOrder:
def transfer_at_index(self, index: QModelIndex) -> TransferOrder:
"""Returns the transfer located at the given index."""
return self.game_model.game.transfers.transfer_at_index(index.row())
@@ -356,11 +372,11 @@ class GameModel:
self.game: Optional[Game] = game
self.transfer_model = TransferModel(self)
if self.game is None:
self.ato_model = AtoModel(self.game, AirTaskingOrder())
self.red_ato_model = AtoModel(self.game, AirTaskingOrder())
self.ato_model = AtoModel(self, AirTaskingOrder())
self.red_ato_model = AtoModel(self, AirTaskingOrder())
else:
self.ato_model = AtoModel(self.game, self.game.blue_ato)
self.red_ato_model = AtoModel(self.game, self.game.red_ato)
self.ato_model = AtoModel(self, self.game.blue_ato)
self.red_ato_model = AtoModel(self, self.game.red_ato)
def set(self, game: Optional[Game]) -> None:
"""Updates the managed Game object.
@@ -371,5 +387,5 @@ class GameModel:
loaded.
"""
self.game = game
self.ato_model.replace_from_game(self.game, player=True)
self.red_ato_model.replace_from_game(self.game, player=False)
self.ato_model.replace_from_game(player=True)
self.red_ato_model.replace_from_game(player=False)