Add on-leave toggle for pilots.

Pilots on leave will not be assignable to any flights (but will not be
unassigned from any already scheduled this turn).

https://github.com/dcs-liberation/dcs_liberation/issues/276
This commit is contained in:
Dan Albert 2021-05-27 17:09:09 -07:00
parent 9a9c351f47
commit 1521f0a9b1
5 changed files with 100 additions and 14 deletions

View File

@ -126,7 +126,7 @@ class Event:
not loss.pilot.player not loss.pilot.player
or not self.game.settings.invulnerable_player_pilots or not self.game.settings.invulnerable_player_pilots
): ):
loss.pilot.alive = False loss.pilot.kill()
aircraft = loss.flight.unit_type aircraft = loss.flight.unit_type
cp = loss.flight.departure cp = loss.flight.departure
available = cp.base.total_units_of_type(aircraft) available = cp.base.total_units_of_type(aircraft)

View File

@ -5,6 +5,7 @@ import logging
import random import random
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import unique, Enum
from pathlib import Path from pathlib import Path
from typing import Type, Tuple, List, TYPE_CHECKING, Optional, Iterable, Iterator from typing import Type, Tuple, List, TYPE_CHECKING, Optional, Iterable, Iterator
@ -25,13 +26,41 @@ class PilotRecord:
missions_flown: int = field(default=0) missions_flown: int = field(default=0)
@unique
class PilotStatus(Enum):
Active = "Active"
OnLeave = "On leave"
Dead = "Dead"
@dataclass @dataclass
class Pilot: class Pilot:
name: str name: str
player: bool = field(default=False) player: bool = field(default=False)
alive: bool = field(default=True) status: PilotStatus = field(default=PilotStatus.Active)
record: PilotRecord = field(default_factory=PilotRecord) record: PilotRecord = field(default_factory=PilotRecord)
@property
def alive(self) -> bool:
return self.status is not PilotStatus.Dead
@property
def on_leave(self) -> bool:
return self.status is PilotStatus.OnLeave
def send_on_leave(self) -> None:
if self.status is not PilotStatus.Active:
raise RuntimeError("Only active pilots may be sent on leave")
self.status = PilotStatus.OnLeave
def return_from_leave(self) -> None:
if self.status is not PilotStatus.OnLeave:
raise RuntimeError("Only pilots on leave may be returned from leave")
self.status = PilotStatus.Active
def kill(self) -> None:
self.status = PilotStatus.Dead
@classmethod @classmethod
def random(cls, faker: Faker) -> Pilot: def random(cls, faker: Faker) -> Pilot:
return Pilot(faker.name()) return Pilot(faker.name())
@ -119,13 +148,20 @@ class Squadron:
def faker(self) -> Faker: def faker(self) -> Faker:
return self.game.faker_for(self.player) return self.game.faker_for(self.player)
def _pilots_with_status(self, status: PilotStatus) -> list[Pilot]:
return [p for p in self.pilots if p.status == status]
@property @property
def active_pilots(self) -> list[Pilot]: def active_pilots(self) -> list[Pilot]:
return [p for p in self.pilots if p.alive] return self._pilots_with_status(PilotStatus.Active)
@property
def pilots_on_leave(self) -> list[Pilot]:
return self._pilots_with_status(PilotStatus.OnLeave)
@property @property
def size(self) -> int: def size(self) -> int:
return len(self.active_pilots) return len(self.active_pilots) + len(self.pilots_on_leave)
def pilot_at_index(self, index: int) -> Pilot: def pilot_at_index(self, index: int) -> Pilot:
return self.pilots[index] return self.pilots[index]

View File

@ -458,6 +458,15 @@ class SquadronModel(QAbstractListModel):
pilot.player = not pilot.player pilot.player = not pilot.player
self.endResetModel() self.endResetModel()
def toggle_leave_state(self, index: QModelIndex) -> None:
pilot = self.pilot_at_index(index)
self.beginResetModel()
if pilot.on_leave:
pilot.return_from_leave()
else:
pilot.send_on_leave()
self.endResetModel()
class GameModel: class GameModel:
"""A model for the Game object. """A model for the Game object.

View File

@ -41,8 +41,9 @@ class SquadronDelegate(TwoColumnRowDelegate):
return self.squadron(index).nickname return self.squadron(index).nickname
elif (row, column) == (1, 1): elif (row, column) == (1, 1):
squadron = self.squadron(index) squadron = self.squadron(index)
active = len(squadron.active_pilots)
available = len(squadron.available_pilots) available = len(squadron.available_pilots)
return f"{squadron.size} active pilots, {available} available" return f"{squadron.size} pilots, {active} active, {available} unassigned"
return "" return ""

View File

@ -12,6 +12,7 @@ from PySide2.QtWidgets import (
QListView, QListView,
QVBoxLayout, QVBoxLayout,
QPushButton, QPushButton,
QHBoxLayout,
) )
from game.squadrons import Pilot from game.squadrons import Pilot
@ -39,7 +40,7 @@ class PilotDelegate(TwoColumnRowDelegate):
elif (row, column) == (1, 0): elif (row, column) == (1, 0):
return "Player" if pilot.player else "AI" return "Player" if pilot.player else "AI"
elif (row, column) == (1, 1): elif (row, column) == (1, 1):
return "Alive" if pilot.alive else "Dead" return pilot.status.value
return "" return ""
@ -80,11 +81,35 @@ class SquadronDialog(QDialog):
) )
layout.addWidget(self.pilot_list) layout.addWidget(self.pilot_list)
button_panel = QHBoxLayout()
button_panel.addStretch()
layout.addLayout(button_panel)
self.toggle_ai_button = QPushButton() self.toggle_ai_button = QPushButton()
self.reset_button_state(self.pilot_list.currentIndex()) self.reset_ai_toggle_state(self.pilot_list.currentIndex())
self.toggle_ai_button.setProperty("style", "start-button") self.toggle_ai_button.setProperty("style", "start-button")
self.toggle_ai_button.clicked.connect(self.toggle_ai) self.toggle_ai_button.clicked.connect(self.toggle_ai)
layout.addWidget(self.toggle_ai_button, alignment=Qt.AlignRight) button_panel.addWidget(self.toggle_ai_button, alignment=Qt.AlignRight)
self.toggle_leave_button = QPushButton()
self.reset_leave_toggle_state(self.pilot_list.currentIndex())
self.toggle_leave_button.setProperty("style", "start-button")
self.toggle_leave_button.clicked.connect(self.toggle_leave)
button_panel.addWidget(self.toggle_leave_button, alignment=Qt.AlignRight)
def check_disabled_button_states(
self, button: QPushButton, index: QModelIndex
) -> bool:
if not index.isValid():
button.setText("No pilot selected")
button.setDisabled(True)
return True
pilot = self.squadron_model.pilot_at_index(index)
if not pilot.alive:
button.setText("Pilot is dead")
button.setDisabled(True)
return True
return False
def toggle_ai(self) -> None: def toggle_ai(self) -> None:
index = self.pilot_list.currentIndex() index = self.pilot_list.currentIndex()
@ -93,23 +118,38 @@ class SquadronDialog(QDialog):
return return
self.squadron_model.toggle_ai_state(index) self.squadron_model.toggle_ai_state(index)
def reset_button_state(self, index: QModelIndex) -> None: def reset_ai_toggle_state(self, index: QModelIndex) -> None:
if self.check_disabled_button_states(self.toggle_ai_button, index):
return
if not self.squadron_model.squadron.aircraft.flyable: if not self.squadron_model.squadron.aircraft.flyable:
self.toggle_ai_button.setText("Not flyable") self.toggle_ai_button.setText("Not flyable")
self.toggle_ai_button.setDisabled(True) self.toggle_ai_button.setDisabled(True)
return return
if not index.isValid():
self.toggle_ai_button.setText("No pilot selected")
self.toggle_ai_button.setDisabled(True)
return
self.toggle_ai_button.setEnabled(True) self.toggle_ai_button.setEnabled(True)
pilot = self.squadron_model.pilot_at_index(index) pilot = self.squadron_model.pilot_at_index(index)
self.toggle_ai_button.setText( self.toggle_ai_button.setText(
"Convert to AI" if pilot.player else "Convert to player" "Convert to AI" if pilot.player else "Convert to player"
) )
def toggle_leave(self) -> None:
index = self.pilot_list.currentIndex()
if not index.isValid():
logging.error("Cannot toggle on leave state: no pilot is selected")
return
self.squadron_model.toggle_leave_state(index)
def reset_leave_toggle_state(self, index: QModelIndex) -> None:
if self.check_disabled_button_states(self.toggle_leave_button, index):
return
pilot = self.squadron_model.pilot_at_index(index)
self.toggle_leave_button.setEnabled(True)
self.toggle_leave_button.setText(
"Return from leave" if pilot.on_leave else "Send on leave"
)
def on_selection_changed( def on_selection_changed(
self, selected: QItemSelection, _deselected: QItemSelection self, selected: QItemSelection, _deselected: QItemSelection
) -> None: ) -> None:
index = selected.indexes()[0] index = selected.indexes()[0]
self.reset_button_state(index) self.reset_ai_toggle_state(index)
self.reset_leave_toggle_state(index)