mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Finish implementation of add/remove squadrons from AirWingConfigurationDialog
Users can now add and remove squadrons with specific buttons. this also allows new aircraft types to be added as well. - rebased existing PR to develop - reverted observable and changed to signals - changed the general concept so that changes only affect the ui data model and not the game directly. Game will only be updated on apply - removed unused code - adopt to review comments - allow user to choose a predefined squadron preset (also alow none value to use the random generator) - Reuse the squadron defs from the default assigner in the AirWing class - allow user to re-roll the squadron nickname (also added new ui icons for the button)
This commit is contained in:
parent
e4ba9a8b72
commit
d2f7785f9f
@ -5,10 +5,8 @@ from typing import Optional, TYPE_CHECKING
|
|||||||
|
|
||||||
from game.squadrons import Squadron
|
from game.squadrons import Squadron
|
||||||
from game.squadrons.squadrondef import SquadronDef
|
from game.squadrons.squadrondef import SquadronDef
|
||||||
from game.squadrons.squadrondefloader import SquadronDefLoader
|
|
||||||
from ..ato.flighttype import FlightType
|
from ..ato.flighttype import FlightType
|
||||||
from .campaignairwingconfig import CampaignAirWingConfig, SquadronConfig
|
from .campaignairwingconfig import CampaignAirWingConfig, SquadronConfig
|
||||||
from .squadrondefgenerator import SquadronDefGenerator
|
|
||||||
from ..dcs.aircrafttype import AircraftType
|
from ..dcs.aircrafttype import AircraftType
|
||||||
from ..theater import ControlPoint
|
from ..theater import ControlPoint
|
||||||
|
|
||||||
@ -25,14 +23,6 @@ class DefaultSquadronAssigner:
|
|||||||
self.game = game
|
self.game = game
|
||||||
self.coalition = coalition
|
self.coalition = coalition
|
||||||
self.air_wing = coalition.air_wing
|
self.air_wing = coalition.air_wing
|
||||||
self.squadron_defs = SquadronDefLoader(game, coalition).load()
|
|
||||||
self.squadron_def_generator = SquadronDefGenerator(self.coalition)
|
|
||||||
|
|
||||||
def claim_squadron_def(self, squadron: SquadronDef) -> None:
|
|
||||||
try:
|
|
||||||
self.squadron_defs[squadron.aircraft].remove(squadron)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def assign(self) -> None:
|
def assign(self) -> None:
|
||||||
for control_point in self.game.theater.control_points_for(
|
for control_point in self.game.theater.control_points_for(
|
||||||
@ -47,7 +37,6 @@ class DefaultSquadronAssigner:
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.claim_squadron_def(squadron_def)
|
|
||||||
squadron = Squadron.create_from(
|
squadron = Squadron.create_from(
|
||||||
squadron_def, control_point, self.coalition, self.game
|
squadron_def, control_point, self.coalition, self.game
|
||||||
)
|
)
|
||||||
@ -74,7 +63,7 @@ class DefaultSquadronAssigner:
|
|||||||
|
|
||||||
# If we can't find any squadron matching the requirement, we should
|
# If we can't find any squadron matching the requirement, we should
|
||||||
# create one.
|
# create one.
|
||||||
return self.squadron_def_generator.generate_for_task(
|
return self.air_wing.squadron_def_generator.generate_for_task(
|
||||||
config.primary, control_point
|
config.primary, control_point
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -105,7 +94,7 @@ class DefaultSquadronAssigner:
|
|||||||
|
|
||||||
# No premade squadron available for this aircraft that meets the requirements,
|
# No premade squadron available for this aircraft that meets the requirements,
|
||||||
# so generate one if possible.
|
# so generate one if possible.
|
||||||
return self.squadron_def_generator.generate_for_aircraft(aircraft)
|
return self.air_wing.squadron_def_generator.generate_for_aircraft(aircraft)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def squadron_compatible_with(
|
def squadron_compatible_with(
|
||||||
@ -121,18 +110,24 @@ class DefaultSquadronAssigner:
|
|||||||
def find_squadron_for_airframe(
|
def find_squadron_for_airframe(
|
||||||
self, aircraft: AircraftType, task: FlightType, control_point: ControlPoint
|
self, aircraft: AircraftType, task: FlightType, control_point: ControlPoint
|
||||||
) -> Optional[SquadronDef]:
|
) -> Optional[SquadronDef]:
|
||||||
for squadron in self.squadron_defs[aircraft]:
|
for squadron in self.air_wing.squadron_defs[aircraft]:
|
||||||
if self.squadron_compatible_with(squadron, task, control_point):
|
if not squadron.claimed and self.squadron_compatible_with(
|
||||||
|
squadron, task, control_point
|
||||||
|
):
|
||||||
return squadron
|
return squadron
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def find_squadron_by_name(
|
def find_squadron_by_name(
|
||||||
self, name: str, task: FlightType, control_point: ControlPoint
|
self, name: str, task: FlightType, control_point: ControlPoint
|
||||||
) -> Optional[SquadronDef]:
|
) -> Optional[SquadronDef]:
|
||||||
for squadrons in self.squadron_defs.values():
|
for squadrons in self.air_wing.squadron_defs.values():
|
||||||
for squadron in squadrons:
|
for squadron in squadrons:
|
||||||
if squadron.name == name and self.squadron_compatible_with(
|
if (
|
||||||
squadron, task, control_point, ignore_base_preference=True
|
not squadron.claimed
|
||||||
|
and squadron.name == name
|
||||||
|
and self.squadron_compatible_with(
|
||||||
|
squadron, task, control_point, ignore_base_preference=True
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return squadron
|
return squadron
|
||||||
return None
|
return None
|
||||||
@ -140,8 +135,10 @@ class DefaultSquadronAssigner:
|
|||||||
def find_squadron_for_task(
|
def find_squadron_for_task(
|
||||||
self, task: FlightType, control_point: ControlPoint
|
self, task: FlightType, control_point: ControlPoint
|
||||||
) -> Optional[SquadronDef]:
|
) -> Optional[SquadronDef]:
|
||||||
for squadrons in self.squadron_defs.values():
|
for squadrons in self.air_wing.squadron_defs.values():
|
||||||
for squadron in squadrons:
|
for squadron in squadrons:
|
||||||
if self.squadron_compatible_with(squadron, task, control_point):
|
if not squadron.claimed and self.squadron_compatible_with(
|
||||||
|
squadron, task, control_point
|
||||||
|
):
|
||||||
return squadron
|
return squadron
|
||||||
return None
|
return None
|
||||||
|
|||||||
@ -12,12 +12,12 @@ from gen.flights.ai_flight_planner_db import aircraft_for_task, tasks_for_aircra
|
|||||||
from game.ato.flighttype import FlightType
|
from game.ato.flighttype import FlightType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.coalition import Coalition
|
from game.factions.faction import Faction
|
||||||
|
|
||||||
|
|
||||||
class SquadronDefGenerator:
|
class SquadronDefGenerator:
|
||||||
def __init__(self, coalition: Coalition) -> None:
|
def __init__(self, faction: Faction) -> None:
|
||||||
self.coalition = coalition
|
self.faction = faction
|
||||||
self.count = itertools.count(1)
|
self.count = itertools.count(1)
|
||||||
self.used_nicknames: set[str] = set()
|
self.used_nicknames: set[str] = set()
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ class SquadronDefGenerator:
|
|||||||
) -> Optional[SquadronDef]:
|
) -> Optional[SquadronDef]:
|
||||||
aircraft_choice: Optional[AircraftType] = None
|
aircraft_choice: Optional[AircraftType] = None
|
||||||
for aircraft in aircraft_for_task(task):
|
for aircraft in aircraft_for_task(task):
|
||||||
if aircraft not in self.coalition.faction.aircrafts:
|
if aircraft not in self.faction.aircrafts:
|
||||||
continue
|
continue
|
||||||
if not control_point.can_operate(aircraft):
|
if not control_point.can_operate(aircraft):
|
||||||
continue
|
continue
|
||||||
@ -44,7 +44,7 @@ class SquadronDefGenerator:
|
|||||||
return SquadronDef(
|
return SquadronDef(
|
||||||
name=f"Squadron {next(self.count):03}",
|
name=f"Squadron {next(self.count):03}",
|
||||||
nickname=self.random_nickname(),
|
nickname=self.random_nickname(),
|
||||||
country=self.coalition.country_name,
|
country=self.faction.country,
|
||||||
role="Flying Squadron",
|
role="Flying Squadron",
|
||||||
aircraft=aircraft,
|
aircraft=aircraft,
|
||||||
livery=None,
|
livery=None,
|
||||||
|
|||||||
@ -40,7 +40,7 @@ class Coalition:
|
|||||||
self.procurement_requests: OrderedSet[AircraftProcurementRequest] = OrderedSet()
|
self.procurement_requests: OrderedSet[AircraftProcurementRequest] = OrderedSet()
|
||||||
self.bullseye = Bullseye(Point(0, 0))
|
self.bullseye = Bullseye(Point(0, 0))
|
||||||
self.faker = Faker(self.faction.locales)
|
self.faker = Faker(self.faction.locales)
|
||||||
self.air_wing = AirWing(player)
|
self.air_wing = AirWing(player, game, self.faction)
|
||||||
self.transfers = PendingTransfers(game, player)
|
self.transfers = PendingTransfers(game, player)
|
||||||
|
|
||||||
# Late initialized because the two coalitions in the game are mutually
|
# Late initialized because the two coalitions in the game are mutually
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
class Event(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Observable(object):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.callbacks = []
|
|
||||||
|
|
||||||
def subscribe(self, callback) -> None:
|
|
||||||
self.callbacks.append(callback)
|
|
||||||
|
|
||||||
def unsubscribe(self, callback) -> None:
|
|
||||||
self.callbacks.remove(callback)
|
|
||||||
|
|
||||||
def fire(self, **attrs) -> None:
|
|
||||||
e = Event()
|
|
||||||
e.source = self
|
|
||||||
for k, v in attrs.items():
|
|
||||||
setattr(e, k, v)
|
|
||||||
for fn in self.callbacks:
|
|
||||||
fn(e)
|
|
||||||
@ -7,41 +7,34 @@ from typing import Sequence, Iterator, TYPE_CHECKING, Optional
|
|||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
from gen.flights.ai_flight_planner_db import aircraft_for_task
|
from gen.flights.ai_flight_planner_db import aircraft_for_task
|
||||||
from gen.flights.closestairfields import ObjectiveDistanceCache
|
from gen.flights.closestairfields import ObjectiveDistanceCache
|
||||||
|
from .squadrondef import SquadronDef
|
||||||
|
from .squadrondefloader import SquadronDefLoader
|
||||||
|
from ..campaignloader.squadrondefgenerator import SquadronDefGenerator
|
||||||
|
from ..factions.faction import Faction
|
||||||
from ..theater import ControlPoint, MissionTarget
|
from ..theater import ControlPoint, MissionTarget
|
||||||
|
|
||||||
from ..observer import Observable
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from game.game import Game
|
||||||
from ..ato.flighttype import FlightType
|
from ..ato.flighttype import FlightType
|
||||||
from .squadron import Squadron
|
from .squadron import Squadron
|
||||||
|
|
||||||
|
|
||||||
class AirWing(Observable):
|
class AirWing:
|
||||||
def __init__(self, player: bool) -> None:
|
def __init__(self, player: bool, game: Game, faction: Faction) -> None:
|
||||||
super().__init__()
|
|
||||||
self.player = player
|
self.player = player
|
||||||
self.squadrons: dict[AircraftType, list[Squadron]] = defaultdict(list)
|
self.squadrons: dict[AircraftType, list[Squadron]] = defaultdict(list)
|
||||||
|
self.squadron_defs = SquadronDefLoader(game, faction).load()
|
||||||
|
self.squadron_def_generator = SquadronDefGenerator(faction)
|
||||||
|
|
||||||
|
def unclaim_squadron_def(self, squadron: Squadron) -> None:
|
||||||
|
if squadron.aircraft in self.squadron_defs:
|
||||||
|
for squadron_def in self.squadron_defs[squadron.aircraft]:
|
||||||
|
if squadron_def.claimed and squadron_def.name == squadron.name:
|
||||||
|
squadron_def.claimed = False
|
||||||
|
|
||||||
def add_squadron(self, squadron: Squadron) -> None:
|
def add_squadron(self, squadron: Squadron) -> None:
|
||||||
new_aircraft_type = squadron.aircraft not in self.squadrons
|
|
||||||
|
|
||||||
self.squadrons[squadron.aircraft].append(squadron)
|
self.squadrons[squadron.aircraft].append(squadron)
|
||||||
|
|
||||||
if new_aircraft_type:
|
|
||||||
self.fire(type="add_aircraft_type", obj=squadron.aircraft)
|
|
||||||
self.fire(type="add_squadron", obj=squadron)
|
|
||||||
|
|
||||||
def remove_squadron(self, toRemove: Squadron) -> None:
|
|
||||||
if toRemove.aircraft in self.squadrons:
|
|
||||||
self.squadrons[toRemove.aircraft].remove(toRemove)
|
|
||||||
self.fire(type="remove_squadron", obj=toRemove)
|
|
||||||
if len(self.squadrons[toRemove.aircraft]) == 0:
|
|
||||||
self.remove_aircraft_type(toRemove.aircraft)
|
|
||||||
|
|
||||||
def remove_aircraft_type(self, toRemove: AircraftType) -> None:
|
|
||||||
self.squadrons.pop(toRemove)
|
|
||||||
self.fire(type="remove_aircraft_type", obj=toRemove)
|
|
||||||
|
|
||||||
def squadrons_for(self, aircraft: AircraftType) -> Sequence[Squadron]:
|
def squadrons_for(self, aircraft: AircraftType) -> Sequence[Squadron]:
|
||||||
return self.squadrons[aircraft]
|
return self.squadrons[aircraft]
|
||||||
|
|
||||||
|
|||||||
@ -418,6 +418,7 @@ class Squadron:
|
|||||||
coalition: Coalition,
|
coalition: Coalition,
|
||||||
game: Game,
|
game: Game,
|
||||||
) -> Squadron:
|
) -> Squadron:
|
||||||
|
squadron_def.claimed = True
|
||||||
return Squadron(
|
return Squadron(
|
||||||
squadron_def.name,
|
squadron_def.name,
|
||||||
squadron_def.nickname,
|
squadron_def.nickname,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class SquadronDef:
|
|||||||
mission_types: tuple[FlightType, ...]
|
mission_types: tuple[FlightType, ...]
|
||||||
operating_bases: OperatingBases
|
operating_bases: OperatingBases
|
||||||
pilot_pool: list[Pilot]
|
pilot_pool: list[Pilot]
|
||||||
|
claimed: bool = False
|
||||||
|
|
||||||
auto_assignable_mission_types: set[FlightType] = field(
|
auto_assignable_mission_types: set[FlightType] = field(
|
||||||
init=False, hash=False, compare=False
|
init=False, hash=False, compare=False
|
||||||
|
|||||||
@ -10,13 +10,13 @@ from .squadrondef import SquadronDef
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game import Game
|
from game import Game
|
||||||
from game.coalition import Coalition
|
from ..factions.faction import Faction
|
||||||
|
|
||||||
|
|
||||||
class SquadronDefLoader:
|
class SquadronDefLoader:
|
||||||
def __init__(self, game: Game, coalition: Coalition) -> None:
|
def __init__(self, game: Game, faction: Faction) -> None:
|
||||||
self.game = game
|
self.game = game
|
||||||
self.coalition = coalition
|
self.faction = faction
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def squadron_directories() -> Iterator[Path]:
|
def squadron_directories() -> Iterator[Path]:
|
||||||
@ -27,8 +27,8 @@ class SquadronDefLoader:
|
|||||||
|
|
||||||
def load(self) -> dict[AircraftType, list[SquadronDef]]:
|
def load(self) -> dict[AircraftType, list[SquadronDef]]:
|
||||||
squadrons: dict[AircraftType, list[SquadronDef]] = defaultdict(list)
|
squadrons: dict[AircraftType, list[SquadronDef]] = defaultdict(list)
|
||||||
country = self.coalition.country_name
|
country = self.faction.country
|
||||||
faction = self.coalition.faction
|
faction = self.faction
|
||||||
any_country = country.startswith("Combined Joint Task Forces ")
|
any_country = country.startswith("Combined Joint Task Forces ")
|
||||||
for directory in self.squadron_directories():
|
for directory in self.squadron_directories():
|
||||||
for path, squadron_def in self.load_squadrons_from(directory):
|
for path, squadron_def in self.load_squadrons_from(directory):
|
||||||
|
|||||||
@ -111,6 +111,9 @@ def load_icons():
|
|||||||
"./resources/ui/misc/" + get_theme_icons() + "/pluginsoptions.png"
|
"./resources/ui/misc/" + get_theme_icons() + "/pluginsoptions.png"
|
||||||
)
|
)
|
||||||
ICONS["Notes"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/notes.png")
|
ICONS["Notes"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/notes.png")
|
||||||
|
ICONS["Reload"] = QPixmap(
|
||||||
|
"./resources/ui/misc/" + get_theme_icons() + "/reload.png"
|
||||||
|
)
|
||||||
|
|
||||||
ICONS["TaskCAS"] = QPixmap("./resources/ui/tasks/cas.png")
|
ICONS["TaskCAS"] = QPixmap("./resources/ui/tasks/cas.png")
|
||||||
ICONS["TaskCAP"] = QPixmap("./resources/ui/tasks/cap.png")
|
ICONS["TaskCAP"] = QPixmap("./resources/ui/tasks/cap.png")
|
||||||
|
|||||||
@ -26,6 +26,7 @@ from PySide2.QtWidgets import (
|
|||||||
QCheckBox,
|
QCheckBox,
|
||||||
QPushButton,
|
QPushButton,
|
||||||
QGridLayout,
|
QGridLayout,
|
||||||
|
QToolButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
from game import Game
|
from game import Game
|
||||||
@ -33,10 +34,9 @@ from game.ato.flighttype import FlightType
|
|||||||
from game.coalition import Coalition
|
from game.coalition import Coalition
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
from game.squadrons import AirWing, Pilot, Squadron
|
from game.squadrons import AirWing, Pilot, Squadron
|
||||||
|
from game.squadrons.squadrondef import SquadronDef
|
||||||
from game.theater import ConflictTheater, ControlPoint
|
from game.theater import ConflictTheater, ControlPoint
|
||||||
from qt_ui.uiconstants import AIRCRAFT_ICONS
|
from qt_ui.uiconstants import AIRCRAFT_ICONS, ICONS
|
||||||
|
|
||||||
from qt_ui.windows.SquadronConfigPopup import SquadronConfigPopup
|
|
||||||
|
|
||||||
|
|
||||||
class AllowedMissionTypeControls(QVBoxLayout):
|
class AllowedMissionTypeControls(QVBoxLayout):
|
||||||
@ -72,7 +72,6 @@ class AllowedMissionTypeControls(QVBoxLayout):
|
|||||||
self.allowed_mission_types.add(task)
|
self.allowed_mission_types.add(task)
|
||||||
else:
|
else:
|
||||||
self.allowed_mission_types.remove(task)
|
self.allowed_mission_types.remove(task)
|
||||||
self.squadron.set_allowed_mission_types(self.allowed_mission_types)
|
|
||||||
|
|
||||||
|
|
||||||
class SquadronBaseSelector(QComboBox):
|
class SquadronBaseSelector(QComboBox):
|
||||||
@ -85,29 +84,39 @@ class SquadronBaseSelector(QComboBox):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
bases: Iterable[ControlPoint],
|
bases: Iterable[ControlPoint],
|
||||||
squadron: Squadron,
|
selected_base: Optional[ControlPoint],
|
||||||
|
aircraft_type: Optional[AircraftType],
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.bases = list(bases)
|
|
||||||
self.squadron = squadron
|
|
||||||
self.setSizeAdjustPolicy(self.AdjustToContents)
|
self.setSizeAdjustPolicy(self.AdjustToContents)
|
||||||
|
self.bases = list(bases)
|
||||||
|
self.set_aircraft_type(aircraft_type)
|
||||||
|
|
||||||
for base in self.bases:
|
if selected_base:
|
||||||
if not base.can_operate(self.squadron.aircraft):
|
self.setCurrentText(selected_base.name)
|
||||||
continue
|
# TODO can we get a prefered base if none is selected?
|
||||||
self.addItem(base.name, base)
|
|
||||||
self.model().sort(0)
|
def set_aircraft_type(self, aircraft_type: Optional[AircraftType]):
|
||||||
self.setCurrentText(self.squadron.location.name)
|
self.clear()
|
||||||
|
if aircraft_type:
|
||||||
|
for base in self.bases:
|
||||||
|
if not base.can_operate(aircraft_type):
|
||||||
|
continue
|
||||||
|
self.addItem(base.name, base)
|
||||||
|
self.model().sort(0)
|
||||||
|
self.setEnabled(True)
|
||||||
|
else:
|
||||||
|
self.addItem("Select aircraft type first", None)
|
||||||
|
self.setEnabled(False)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
|
||||||
class SquadronConfigurationBox(QGroupBox):
|
class SquadronConfigurationBox(QGroupBox):
|
||||||
def __init__(
|
remove_squadron_signal = Signal(Squadron)
|
||||||
self, squadron: Squadron, theater: ConflictTheater, air_wing: AirWing
|
|
||||||
) -> None:
|
def __init__(self, squadron: Squadron, theater: ConflictTheater) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setCheckable(False)
|
|
||||||
self.squadron = squadron
|
self.squadron = squadron
|
||||||
self.air_wing = air_wing
|
|
||||||
self.reset_title()
|
self.reset_title()
|
||||||
|
|
||||||
columns = QHBoxLayout()
|
columns = QHBoxLayout()
|
||||||
@ -121,14 +130,24 @@ class SquadronConfigurationBox(QGroupBox):
|
|||||||
self.name_edit.textChanged.connect(self.on_name_changed)
|
self.name_edit.textChanged.connect(self.on_name_changed)
|
||||||
left_column.addWidget(self.name_edit)
|
left_column.addWidget(self.name_edit)
|
||||||
|
|
||||||
left_column.addWidget(QLabel("Nickname:"))
|
nickname_edit_layout = QGridLayout()
|
||||||
|
left_column.addLayout(nickname_edit_layout)
|
||||||
|
|
||||||
|
nickname_edit_layout.addWidget(QLabel("Nickname:"), 0, 0, 1, 2)
|
||||||
self.nickname_edit = QLineEdit(squadron.nickname)
|
self.nickname_edit = QLineEdit(squadron.nickname)
|
||||||
self.nickname_edit.textChanged.connect(self.on_nickname_changed)
|
self.nickname_edit.textChanged.connect(self.on_nickname_changed)
|
||||||
left_column.addWidget(self.nickname_edit)
|
nickname_edit_layout.addWidget(self.nickname_edit, 1, 0, Qt.AlignTop)
|
||||||
|
reroll_nickname_button = QToolButton()
|
||||||
|
reroll_nickname_button.setIcon(QIcon(ICONS["Reload"]))
|
||||||
|
reroll_nickname_button.setToolTip("Re-roll nickname")
|
||||||
|
reroll_nickname_button.clicked.connect(self.reroll_nickname)
|
||||||
|
nickname_edit_layout.addWidget(reroll_nickname_button, 1, 1, Qt.AlignTop)
|
||||||
|
|
||||||
left_column.addWidget(QLabel("Base:"))
|
left_column.addWidget(QLabel("Base:"))
|
||||||
self.base_selector = SquadronBaseSelector(
|
self.base_selector = SquadronBaseSelector(
|
||||||
theater.control_points_for(squadron.player), squadron
|
theater.control_points_for(squadron.player),
|
||||||
|
squadron.location,
|
||||||
|
squadron.aircraft,
|
||||||
)
|
)
|
||||||
self.base_selector.currentIndexChanged.connect(self.on_base_changed)
|
self.base_selector.currentIndexChanged.connect(self.on_base_changed)
|
||||||
left_column.addWidget(self.base_selector)
|
left_column.addWidget(self.base_selector)
|
||||||
@ -150,20 +169,18 @@ class SquadronConfigurationBox(QGroupBox):
|
|||||||
self.player_list.setAcceptRichText(False)
|
self.player_list.setAcceptRichText(False)
|
||||||
self.player_list.setEnabled(squadron.player)
|
self.player_list.setEnabled(squadron.player)
|
||||||
left_column.addWidget(self.player_list)
|
left_column.addWidget(self.player_list)
|
||||||
self.player_list.textChanged.connect(self.on_pilots_changed)
|
delete_button = QPushButton("Remove Squadron")
|
||||||
|
delete_button.setMaximumWidth(140)
|
||||||
delete_button = QPushButton("Remove")
|
delete_button.clicked.connect(self.remove_from_squadron_config)
|
||||||
delete_button.setMaximumWidth(80)
|
|
||||||
delete_button.clicked.connect(
|
|
||||||
lambda state: self.air_wing.remove_squadron(self.squadron)
|
|
||||||
)
|
|
||||||
left_column.addWidget(delete_button)
|
left_column.addWidget(delete_button)
|
||||||
|
|
||||||
left_column.addStretch()
|
left_column.addStretch()
|
||||||
|
|
||||||
self.allowed_missions = AllowedMissionTypeControls(squadron)
|
self.allowed_missions = AllowedMissionTypeControls(squadron)
|
||||||
columns.addLayout(self.allowed_missions)
|
columns.addLayout(self.allowed_missions)
|
||||||
|
|
||||||
|
def remove_from_squadron_config(self) -> None:
|
||||||
|
self.remove_squadron_signal.emit(self.squadron)
|
||||||
|
|
||||||
def on_name_changed(self, text: str) -> None:
|
def on_name_changed(self, text: str) -> None:
|
||||||
self.squadron.name = text
|
self.squadron.name = text
|
||||||
self.reset_title()
|
self.reset_title()
|
||||||
@ -180,56 +197,66 @@ class SquadronConfigurationBox(QGroupBox):
|
|||||||
def reset_title(self) -> None:
|
def reset_title(self) -> None:
|
||||||
self.setTitle(f"{self.squadron.name} - {self.squadron.aircraft}")
|
self.setTitle(f"{self.squadron.name} - {self.squadron.aircraft}")
|
||||||
|
|
||||||
def on_pilots_changed(self) -> None:
|
def reroll_nickname(self) -> None:
|
||||||
|
self.nickname_edit.setText(
|
||||||
|
self.squadron.coalition.air_wing.squadron_def_generator.random_nickname()
|
||||||
|
)
|
||||||
|
|
||||||
|
def apply(self) -> Squadron:
|
||||||
player_names = self.player_list.toPlainText().splitlines()
|
player_names = self.player_list.toPlainText().splitlines()
|
||||||
# Prepend player pilots so they get set active first.
|
# Prepend player pilots so they get set active first.
|
||||||
self.squadron.pilot_pool = [
|
self.squadron.pilot_pool = [
|
||||||
Pilot(n, player=True) for n in player_names
|
Pilot(n, player=True) for n in player_names
|
||||||
] + self.squadron.pilot_pool
|
] + self.squadron.pilot_pool
|
||||||
|
self.squadron.set_allowed_mission_types(
|
||||||
|
self.allowed_missions.allowed_mission_types
|
||||||
|
)
|
||||||
|
return self.squadron
|
||||||
|
|
||||||
|
|
||||||
class SquadronConfigurationLayout(QVBoxLayout):
|
class SquadronConfigurationLayout(QVBoxLayout):
|
||||||
def __init__(
|
config_changed = Signal(AircraftType)
|
||||||
self, squadrons: list[Squadron], theater: ConflictTheater, air_wing: AirWing
|
|
||||||
) -> None:
|
def __init__(self, squadrons: list[Squadron], theater: ConflictTheater) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.squadron_configs = []
|
||||||
self.theater = theater
|
self.theater = theater
|
||||||
self.air_wing = air_wing
|
|
||||||
self.squadron_configs: dict[Squadron, SquadronConfigurationBox] = {}
|
|
||||||
for squadron in squadrons:
|
for squadron in squadrons:
|
||||||
squadron_config = SquadronConfigurationBox(
|
self.add_squadron(squadron)
|
||||||
squadron, self.theater, self.air_wing
|
|
||||||
)
|
|
||||||
self.squadron_configs[squadron] = squadron_config
|
|
||||||
self.addWidget(squadron_config)
|
|
||||||
|
|
||||||
def addSquadron(self, squadron: Squadron) -> None:
|
def apply(self) -> list[Squadron]:
|
||||||
if squadron not in self.squadron_configs:
|
keep_squadrons = []
|
||||||
squadron_config = SquadronConfigurationBox(
|
for squadron_config in self.squadron_configs:
|
||||||
squadron, self.theater, self.air_wing
|
keep_squadrons.append(squadron_config.apply())
|
||||||
)
|
return keep_squadrons
|
||||||
self.squadron_configs[squadron] = squadron_config
|
|
||||||
self.addWidget(squadron_config)
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def removeSquadron(self, squadron: Squadron) -> None:
|
def remove_squadron(self, squadron: Squadron) -> None:
|
||||||
if squadron in self.squadron_configs:
|
for squadron_config in self.squadron_configs:
|
||||||
self.removeWidget(self.squadron_configs[squadron])
|
if squadron_config.squadron == squadron:
|
||||||
self.squadron_configs.pop(squadron)
|
squadron_config.deleteLater()
|
||||||
self.update()
|
self.squadron_configs.remove(squadron_config)
|
||||||
|
squadron.coalition.air_wing.unclaim_squadron_def(squadron)
|
||||||
|
self.update()
|
||||||
|
self.config_changed.emit(squadron.aircraft)
|
||||||
|
return
|
||||||
|
|
||||||
|
def add_squadron(self, squadron: Squadron) -> None:
|
||||||
|
squadron_config = SquadronConfigurationBox(squadron, self.theater)
|
||||||
|
squadron_config.remove_squadron_signal.connect(self.remove_squadron)
|
||||||
|
self.squadron_configs.append(squadron_config)
|
||||||
|
self.addWidget(squadron_config)
|
||||||
|
|
||||||
|
|
||||||
class AircraftSquadronsPage(QWidget):
|
class AircraftSquadronsPage(QWidget):
|
||||||
def __init__(
|
remove_squadron_page = Signal(AircraftType)
|
||||||
self, squadrons: list[Squadron], theater: ConflictTheater, air_wing: AirWing
|
|
||||||
) -> None:
|
def __init__(self, squadrons: list[Squadron], theater: ConflictTheater) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
self.squadrons_config = SquadronConfigurationLayout(
|
self.squadrons_config = SquadronConfigurationLayout(squadrons, theater)
|
||||||
squadrons, theater, air_wing
|
self.squadrons_config.config_changed.connect(self.on_squadron_config_changed)
|
||||||
)
|
|
||||||
|
|
||||||
scrolling_widget = QWidget()
|
scrolling_widget = QWidget()
|
||||||
scrolling_widget.setLayout(self.squadrons_config)
|
scrolling_widget.setLayout(self.squadrons_config)
|
||||||
@ -242,51 +269,59 @@ class AircraftSquadronsPage(QWidget):
|
|||||||
|
|
||||||
layout.addWidget(scrolling_area)
|
layout.addWidget(scrolling_area)
|
||||||
|
|
||||||
def addSquadron(self, squadron: Squadron) -> None:
|
def on_squadron_config_changed(self, aircraft_type: AircraftType):
|
||||||
self.squadrons_config.addSquadron(squadron)
|
if len(self.squadrons_config.squadron_configs) == 0:
|
||||||
|
self.remove_squadron_page.emit(aircraft_type)
|
||||||
|
|
||||||
def removeSquadron(self, squadron: Squadron) -> None:
|
def add_squadron_to_page(self, squadron: Squadron):
|
||||||
self.squadrons_config.removeSquadron(squadron)
|
self.squadrons_config.add_squadron(squadron)
|
||||||
|
|
||||||
|
def apply(self) -> list[Squadron]:
|
||||||
|
return self.squadrons_config.apply()
|
||||||
|
|
||||||
|
|
||||||
class AircraftSquadronsPanel(QStackedLayout):
|
class AircraftSquadronsPanel(QStackedLayout):
|
||||||
|
page_removed = Signal(AircraftType)
|
||||||
|
|
||||||
def __init__(self, air_wing: AirWing, theater: ConflictTheater) -> None:
|
def __init__(self, air_wing: AirWing, theater: ConflictTheater) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.air_wing = air_wing
|
self.air_wing = air_wing
|
||||||
self.theater = theater
|
self.theater = theater
|
||||||
self.air_wing.subscribe(self.handleChanges)
|
|
||||||
|
|
||||||
self.squadrons_pages: dict[AircraftType, AircraftSquadronsPage] = {}
|
self.squadrons_pages: dict[AircraftType, AircraftSquadronsPage] = {}
|
||||||
for aircraft, squadrons in self.air_wing.squadrons.items():
|
for aircraft, squadrons in self.air_wing.squadrons.items():
|
||||||
page = AircraftSquadronsPage(squadrons, self.theater, self.air_wing)
|
self.new_page_for_type(aircraft, squadrons)
|
||||||
self.addWidget(page)
|
|
||||||
self.squadrons_pages[aircraft] = page
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def remove_page_for_type(self, aircraft_type: AircraftType):
|
||||||
self.air_wing.unsubscribe(self.handleChanges)
|
page = self.squadrons_pages[aircraft_type]
|
||||||
|
self.removeWidget(page)
|
||||||
def handleChanges(self, event) -> None:
|
page.deleteLater()
|
||||||
if event.type == "add_aircraft_type":
|
self.squadrons_pages.pop(aircraft_type)
|
||||||
aircraft_type = event.obj
|
self.page_removed.emit(aircraft_type)
|
||||||
if aircraft_type not in self.squadrons_pages:
|
|
||||||
page = AircraftSquadronsPage(
|
|
||||||
self.air_wing.squadrons[aircraft_type], self.theater, self.air_wing
|
|
||||||
)
|
|
||||||
self.addWidget(page)
|
|
||||||
self.squadrons_pages[aircraft_type] = page
|
|
||||||
elif event.type == "remove_aircraft_type":
|
|
||||||
aircraft_type = event.obj
|
|
||||||
if aircraft_type in self.squadrons_pages:
|
|
||||||
self.removeWidget(self.squadrons_pages[aircraft_type])
|
|
||||||
self.squadrons_pages.pop(aircraft_type)
|
|
||||||
elif event.type == "add_squadron":
|
|
||||||
squadron = event.obj
|
|
||||||
self.squadrons_pages[squadron.aircraft].addSquadron(squadron)
|
|
||||||
elif event.type == "remove_squadron":
|
|
||||||
squadron = event.obj
|
|
||||||
self.squadrons_pages[squadron.aircraft].removeSquadron(squadron)
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def new_page_for_type(
|
||||||
|
self, aircraft_type: AircraftType, squadrons: list[Squadron]
|
||||||
|
) -> None:
|
||||||
|
page = AircraftSquadronsPage(squadrons, self.theater)
|
||||||
|
page.remove_squadron_page.connect(self.remove_page_for_type)
|
||||||
|
self.addWidget(page)
|
||||||
|
self.squadrons_pages[aircraft_type] = page
|
||||||
|
|
||||||
|
def add_squadron_to_panel(self, squadron: Squadron):
|
||||||
|
# Find existing page or add new one
|
||||||
|
if squadron.aircraft in self.squadrons_pages:
|
||||||
|
page = self.squadrons_pages[squadron.aircraft]
|
||||||
|
page.add_squadron_to_page(squadron)
|
||||||
|
else:
|
||||||
|
self.new_page_for_type(squadron.aircraft, [squadron])
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def apply(self) -> None:
|
||||||
|
self.air_wing.squadrons = {}
|
||||||
|
for aircraft, page in self.squadrons_pages.items():
|
||||||
|
self.air_wing.squadrons[aircraft] = page.apply()
|
||||||
|
|
||||||
|
|
||||||
class AircraftTypeList(QListView):
|
class AircraftTypeList(QListView):
|
||||||
page_index_changed = Signal(int)
|
page_index_changed = Signal(int)
|
||||||
@ -296,47 +331,29 @@ class AircraftTypeList(QListView):
|
|||||||
self.setIconSize(QSize(91, 24))
|
self.setIconSize(QSize(91, 24))
|
||||||
self.setMinimumWidth(300)
|
self.setMinimumWidth(300)
|
||||||
|
|
||||||
self.air_wing = air_wing
|
|
||||||
|
|
||||||
self.item_model = QStandardItemModel(self)
|
self.item_model = QStandardItemModel(self)
|
||||||
self.setModel(self.item_model)
|
self.setModel(self.item_model)
|
||||||
|
|
||||||
for aircraft in self.air_wing.squadrons:
|
|
||||||
aircraft_item = QStandardItem(aircraft.name)
|
|
||||||
icon = self.icon_for(aircraft)
|
|
||||||
if icon is not None:
|
|
||||||
aircraft_item.setIcon(icon)
|
|
||||||
aircraft_item.setEditable(False)
|
|
||||||
aircraft_item.setSelectable(True)
|
|
||||||
self.item_model.appendRow(aircraft_item)
|
|
||||||
|
|
||||||
self.selectionModel().setCurrentIndex(
|
self.selectionModel().setCurrentIndex(
|
||||||
self.item_model.index(0, 0), QItemSelectionModel.Select
|
self.item_model.index(0, 0), QItemSelectionModel.Select
|
||||||
)
|
)
|
||||||
self.selectionModel().selectionChanged.connect(self.on_selection_changed)
|
self.selectionModel().selectionChanged.connect(self.on_selection_changed)
|
||||||
|
for aircraft in air_wing.squadrons:
|
||||||
|
self.add_aircraft_type(aircraft)
|
||||||
|
|
||||||
self.air_wing.subscribe(self.handleChanges)
|
def remove_aircraft_type(self, aircraft: AircraftType):
|
||||||
|
for item in self.item_model.findItems(aircraft.name):
|
||||||
|
self.item_model.removeRow(item.row())
|
||||||
|
self.page_index_changed.emit(self.selectionModel().currentIndex().row())
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def add_aircraft_type(self, aircraft: AircraftType):
|
||||||
self.air_wing.unsubscribe(self.handleChanges)
|
aircraft_item = QStandardItem(aircraft.name)
|
||||||
|
icon = self.icon_for(aircraft)
|
||||||
def handleChanges(self, event) -> None:
|
if icon is not None:
|
||||||
if event.type == "remove_aircraft_type":
|
aircraft_item.setIcon(icon)
|
||||||
aircraft_type = event.obj
|
aircraft_item.setEditable(False)
|
||||||
items = self.item_model.findItems(aircraft_type.name)
|
aircraft_item.setSelectable(True)
|
||||||
if len(items) == 1:
|
self.item_model.appendRow(aircraft_item)
|
||||||
for item in items:
|
|
||||||
self.item_model.takeRow(item.row())
|
|
||||||
elif event.type == "add_aircraft_type":
|
|
||||||
aircraft_type = event.obj
|
|
||||||
aircraft_item = QStandardItem(aircraft_type.name)
|
|
||||||
icon = self.icon_for(aircraft_type)
|
|
||||||
if icon is not None:
|
|
||||||
aircraft_item.setIcon(icon)
|
|
||||||
aircraft_item.setEditable(False)
|
|
||||||
aircraft_item.setSelectable(True)
|
|
||||||
self.item_model.appendRow(aircraft_item)
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def on_selection_changed(
|
def on_selection_changed(
|
||||||
self, selected: QItemSelection, _deselected: QItemSelection
|
self, selected: QItemSelection, _deselected: QItemSelection
|
||||||
@ -348,18 +365,6 @@ class AircraftTypeList(QListView):
|
|||||||
return
|
return
|
||||||
self.page_index_changed.emit(indexes[0].row())
|
self.page_index_changed.emit(indexes[0].row())
|
||||||
|
|
||||||
def deleteSelectedType(self) -> None:
|
|
||||||
if self.selectionModel().currentIndex().isValid():
|
|
||||||
aircraftName = str(self.selectionModel().currentIndex().data())
|
|
||||||
to_remove = None
|
|
||||||
for type in self.air_wing.squadrons:
|
|
||||||
if str(type) == aircraftName:
|
|
||||||
to_remove = type
|
|
||||||
if to_remove != None:
|
|
||||||
self.air_wing.remove_aircraft_type(to_remove)
|
|
||||||
else:
|
|
||||||
raise RuntimeError("No aircraft was selected for removal")
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def icon_for(aircraft: AircraftType) -> Optional[QIcon]:
|
def icon_for(aircraft: AircraftType) -> Optional[QIcon]:
|
||||||
name = aircraft.dcs_id
|
name = aircraft.dcs_id
|
||||||
@ -369,37 +374,69 @@ class AircraftTypeList(QListView):
|
|||||||
|
|
||||||
|
|
||||||
class AirWingConfigurationTab(QWidget):
|
class AirWingConfigurationTab(QWidget):
|
||||||
def __init__(
|
def __init__(self, coalition: Coalition, game: Game) -> None:
|
||||||
self, coalition: Coalition, theater: ConflictTheater, game: Game
|
|
||||||
) -> None:
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.game = game
|
|
||||||
self.theater = theater
|
|
||||||
self.coalition = coalition
|
|
||||||
self.air_wing = coalition.air_wing
|
|
||||||
|
|
||||||
layout = QGridLayout()
|
layout = QGridLayout()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
self.game = game
|
||||||
|
self.coalition = coalition
|
||||||
|
|
||||||
self.type_list = AircraftTypeList(self.air_wing)
|
self.type_list = AircraftTypeList(coalition.air_wing)
|
||||||
|
|
||||||
layout.addWidget(self.type_list, 1, 1, 1, 2)
|
layout.addWidget(self.type_list, 1, 1, 1, 2)
|
||||||
|
|
||||||
add_button = QPushButton("Add Aircraft/Squadron")
|
add_button = QPushButton("Add Squadron")
|
||||||
add_button.clicked.connect(lambda state: self.addAircraftType())
|
add_button.clicked.connect(lambda state: self.add_squadron())
|
||||||
layout.addWidget(add_button, 2, 1, 1, 1)
|
layout.addWidget(add_button, 2, 1, 1, 1)
|
||||||
|
|
||||||
remove_button = QPushButton("Remove Aircraft")
|
self.squadrons_panel = AircraftSquadronsPanel(coalition.air_wing, game.theater)
|
||||||
remove_button.clicked.connect(lambda state: self.type_list.deleteSelectedType())
|
self.squadrons_panel.page_removed.connect(self.type_list.remove_aircraft_type)
|
||||||
layout.addWidget(remove_button, 2, 2, 1, 1)
|
|
||||||
|
|
||||||
self.squadrons_panel = AircraftSquadronsPanel(self.air_wing, self.theater)
|
|
||||||
layout.addLayout(self.squadrons_panel, 1, 3, 2, 1)
|
layout.addLayout(self.squadrons_panel, 1, 3, 2, 1)
|
||||||
|
|
||||||
self.type_list.page_index_changed.connect(self.squadrons_panel.setCurrentIndex)
|
self.type_list.page_index_changed.connect(self.squadrons_panel.setCurrentIndex)
|
||||||
|
|
||||||
def addAircraftType(self) -> None:
|
def add_squadron(self) -> None:
|
||||||
SquadronConfigPopup(self.coalition, self.theater, self.game).exec_()
|
selected_aircraft = None
|
||||||
|
if self.type_list.selectionModel().currentIndex().row() >= 0:
|
||||||
|
selected_aircraft = self.type_list.item_model.item(
|
||||||
|
self.type_list.selectionModel().currentIndex().row()
|
||||||
|
).text()
|
||||||
|
|
||||||
|
popup = SquadronConfigPopup(
|
||||||
|
selected_aircraft,
|
||||||
|
self.coalition.faction.aircrafts,
|
||||||
|
list(self.game.theater.control_points_for(self.coalition.player)),
|
||||||
|
self.coalition.air_wing.squadron_defs,
|
||||||
|
)
|
||||||
|
if popup.exec_() != QDialog.Accepted:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_type = popup.aircraft_type_selector.currentData()
|
||||||
|
selected_base = popup.squadron_base_selector.currentData()
|
||||||
|
selected_def = popup.squadron_def_selector.currentData()
|
||||||
|
|
||||||
|
# Let user choose the preset or generate one
|
||||||
|
squadron_def = (
|
||||||
|
selected_def
|
||||||
|
or self.coalition.air_wing.squadron_def_generator.generate_for_aircraft(
|
||||||
|
selected_type
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
squadron = Squadron.create_from(
|
||||||
|
squadron_def, selected_base, self.coalition, self.game
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add Squadron
|
||||||
|
if not self.type_list.item_model.findItems(selected_type.name):
|
||||||
|
self.type_list.add_aircraft_type(selected_type)
|
||||||
|
# TODO Select the newly added type
|
||||||
|
self.squadrons_panel.add_squadron_to_panel(squadron)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def apply(self) -> None:
|
||||||
|
self.squadrons_panel.apply()
|
||||||
|
|
||||||
|
|
||||||
class AirWingConfigurationDialog(QDialog):
|
class AirWingConfigurationDialog(QDialog):
|
||||||
@ -414,22 +451,13 @@ class AirWingConfigurationDialog(QDialog):
|
|||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
doc_url = (
|
|
||||||
"https://github.com/dcs-liberation/dcs_liberation/wiki/Squadrons-and-pilots"
|
|
||||||
)
|
|
||||||
doc_label = QLabel(
|
doc_label = QLabel(
|
||||||
"Use this opportunity to customize the squadrons available to your "
|
"Use this opportunity to customize the squadrons available to your "
|
||||||
"coalition. <strong>This is your only opportunity to make changes.</strong>"
|
"coalition. <strong>This is your only opportunity to make changes.</strong>"
|
||||||
"<br /><br />"
|
"<br /><br />"
|
||||||
"To accept your changes and continue, close this window.<br />"
|
"To accept your changes and continue, close this window."
|
||||||
"<br />"
|
|
||||||
"To remove a squadron from the game, uncheck the box in the title. New "
|
|
||||||
"squadrons cannot be added via the UI at this time. To add a custom "
|
|
||||||
"squadron,<br />"
|
|
||||||
f'see <a style="color:#ffffff" href="{doc_url}">the wiki</a>.'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
doc_label.setOpenExternalLinks(True)
|
|
||||||
layout.addWidget(doc_label)
|
layout.addWidget(doc_label)
|
||||||
|
|
||||||
tab_widget = QTabWidget()
|
tab_widget = QTabWidget()
|
||||||
@ -437,10 +465,113 @@ class AirWingConfigurationDialog(QDialog):
|
|||||||
|
|
||||||
self.tabs = []
|
self.tabs = []
|
||||||
for coalition in game.coalitions:
|
for coalition in game.coalitions:
|
||||||
coalition_tab = AirWingConfigurationTab(coalition, game.theater, game)
|
coalition_tab = AirWingConfigurationTab(coalition, game)
|
||||||
name = "Blue" if coalition.player else "Red"
|
name = "Blue" if coalition.player else "Red"
|
||||||
tab_widget.addTab(coalition_tab, name)
|
tab_widget.addTab(coalition_tab, name)
|
||||||
self.tabs.append(coalition_tab)
|
self.tabs.append(coalition_tab)
|
||||||
|
|
||||||
def reject(self) -> None:
|
def reject(self) -> None:
|
||||||
|
for tab in self.tabs:
|
||||||
|
tab.apply()
|
||||||
super().reject()
|
super().reject()
|
||||||
|
|
||||||
|
|
||||||
|
class SquadronAircraftTypeSelector(QComboBox):
|
||||||
|
def __init__(
|
||||||
|
self, types: list[AircraftType], selected_aircraft: Optional[str]
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.setSizeAdjustPolicy(self.AdjustToContents)
|
||||||
|
|
||||||
|
for type in sorted(types, key=lambda type: type.name):
|
||||||
|
self.addItem(type.name, type)
|
||||||
|
|
||||||
|
if selected_aircraft:
|
||||||
|
self.setCurrentText(selected_aircraft)
|
||||||
|
|
||||||
|
|
||||||
|
class SquadronDefSelector(QComboBox):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
squadron_defs: dict[AircraftType, list[SquadronDef]],
|
||||||
|
aircraft: Optional[AircraftType],
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.setSizeAdjustPolicy(self.AdjustToContents)
|
||||||
|
self.squadron_defs = squadron_defs
|
||||||
|
self.set_aircraft_type(aircraft)
|
||||||
|
|
||||||
|
def set_aircraft_type(self, aircraft: Optional[AircraftType]):
|
||||||
|
self.clear()
|
||||||
|
self.addItem("None (Random)", None)
|
||||||
|
if aircraft and aircraft in self.squadron_defs:
|
||||||
|
for squadron_def in sorted(
|
||||||
|
self.squadron_defs[aircraft], key=lambda squadron_def: squadron_def.name
|
||||||
|
):
|
||||||
|
if not squadron_def.claimed:
|
||||||
|
squadron_name = squadron_def.name
|
||||||
|
if squadron_def.nickname:
|
||||||
|
squadron_name += " (" + squadron_def.nickname + ")"
|
||||||
|
self.addItem(squadron_name, squadron_def)
|
||||||
|
self.setCurrentText("None (Random)")
|
||||||
|
|
||||||
|
|
||||||
|
class SquadronConfigPopup(QDialog):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
selected_aircraft: Optional[str],
|
||||||
|
types: list[AircraftType],
|
||||||
|
bases: list[ControlPoint],
|
||||||
|
squadron_defs: dict[AircraftType, list[SquadronDef]],
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.setWindowTitle(f"Add new Squadron")
|
||||||
|
|
||||||
|
self.column = QVBoxLayout()
|
||||||
|
self.setLayout(self.column)
|
||||||
|
|
||||||
|
self.bases = bases
|
||||||
|
|
||||||
|
self.column.addWidget(QLabel("Aircraft:"))
|
||||||
|
self.aircraft_type_selector = SquadronAircraftTypeSelector(
|
||||||
|
types, selected_aircraft
|
||||||
|
)
|
||||||
|
self.aircraft_type_selector.currentIndexChanged.connect(
|
||||||
|
self.on_aircraft_selection
|
||||||
|
)
|
||||||
|
self.column.addWidget(self.aircraft_type_selector)
|
||||||
|
|
||||||
|
self.column.addWidget(QLabel("Base:"))
|
||||||
|
self.squadron_base_selector = SquadronBaseSelector(
|
||||||
|
bases, None, self.aircraft_type_selector.currentData()
|
||||||
|
)
|
||||||
|
self.column.addWidget(self.squadron_base_selector)
|
||||||
|
|
||||||
|
self.column.addWidget(QLabel("Preset:"))
|
||||||
|
self.squadron_def_selector = SquadronDefSelector(
|
||||||
|
squadron_defs, self.aircraft_type_selector.currentData()
|
||||||
|
)
|
||||||
|
self.column.addWidget(self.squadron_def_selector)
|
||||||
|
|
||||||
|
self.column.addStretch()
|
||||||
|
|
||||||
|
self.button_layout = QHBoxLayout()
|
||||||
|
self.column.addLayout(self.button_layout)
|
||||||
|
|
||||||
|
self.accept_button = QPushButton("Accept")
|
||||||
|
self.accept_button.clicked.connect(lambda state: self.accept())
|
||||||
|
self.button_layout.addWidget(self.accept_button)
|
||||||
|
|
||||||
|
self.cancel_button = QPushButton("Cancel")
|
||||||
|
self.cancel_button.clicked.connect(lambda state: self.reject())
|
||||||
|
self.button_layout.addWidget(self.cancel_button)
|
||||||
|
|
||||||
|
def on_aircraft_selection(self) -> None:
|
||||||
|
self.squadron_base_selector.set_aircraft_type(
|
||||||
|
self.aircraft_type_selector.currentData()
|
||||||
|
)
|
||||||
|
self.squadron_def_selector.set_aircraft_type(
|
||||||
|
self.aircraft_type_selector.currentData()
|
||||||
|
)
|
||||||
|
self.update()
|
||||||
|
|||||||
@ -1,145 +0,0 @@
|
|||||||
from typing import Optional, Callable, Iterable
|
|
||||||
|
|
||||||
from PySide2.QtWidgets import (
|
|
||||||
QDialog,
|
|
||||||
QPushButton,
|
|
||||||
QVBoxLayout,
|
|
||||||
QLabel,
|
|
||||||
QLineEdit,
|
|
||||||
QTextEdit,
|
|
||||||
QCheckBox,
|
|
||||||
QHBoxLayout,
|
|
||||||
QComboBox,
|
|
||||||
)
|
|
||||||
|
|
||||||
from game.dcs.aircrafttype import AircraftType
|
|
||||||
from game.game import Game
|
|
||||||
from game.squadrons import Squadron
|
|
||||||
from game.theater import ConflictTheater, ControlPoint
|
|
||||||
from game.coalition import Coalition
|
|
||||||
from game.factions.faction import Faction
|
|
||||||
from game.campaignloader.squadrondefgenerator import SquadronDefGenerator
|
|
||||||
|
|
||||||
from gen.flights.flight import FlightType
|
|
||||||
|
|
||||||
|
|
||||||
class AircraftTypeSelector(QComboBox):
|
|
||||||
def __init__(self, faction: Faction) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self.types = faction.aircrafts
|
|
||||||
self.setSizeAdjustPolicy(self.AdjustToContents)
|
|
||||||
|
|
||||||
self.addItem("Select aircraft type...", None)
|
|
||||||
self.setCurrentText("Select aircraft type...")
|
|
||||||
self.types.sort(key=str)
|
|
||||||
|
|
||||||
for type in self.types:
|
|
||||||
self.addItem(type.name, type)
|
|
||||||
|
|
||||||
|
|
||||||
class SquadronConfigPopup(QDialog):
|
|
||||||
def __init__(
|
|
||||||
self, coalition: Coalition, theater: ConflictTheater, game: Game
|
|
||||||
) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self.game = game
|
|
||||||
self.coalition = coalition
|
|
||||||
self.theater = theater
|
|
||||||
self.squadron = None
|
|
||||||
|
|
||||||
# self.setMinimumSize(500, 800)
|
|
||||||
self.setWindowTitle(f"Add new Squadron")
|
|
||||||
|
|
||||||
self.column = QVBoxLayout()
|
|
||||||
self.setLayout(self.column)
|
|
||||||
|
|
||||||
self.aircraft_type_selector = AircraftTypeSelector(coalition.faction)
|
|
||||||
self.column.addWidget(self.aircraft_type_selector)
|
|
||||||
|
|
||||||
self.column.addWidget(QLabel("Name:"))
|
|
||||||
self.name_edit = QLineEdit("---")
|
|
||||||
self.name_edit.setEnabled(False)
|
|
||||||
self.name_edit.textChanged.connect(self.on_name_changed)
|
|
||||||
self.column.addWidget(self.name_edit)
|
|
||||||
|
|
||||||
self.column.addWidget(QLabel("Nickname:"))
|
|
||||||
self.nickname_edit = QLineEdit("---")
|
|
||||||
self.nickname_edit.setEnabled(False)
|
|
||||||
self.nickname_edit.textChanged.connect(self.on_nickname_changed)
|
|
||||||
self.column.addWidget(self.nickname_edit)
|
|
||||||
|
|
||||||
self.column.addStretch()
|
|
||||||
self.aircraft_type_selector.currentIndexChanged.connect(
|
|
||||||
self.on_aircraft_selection
|
|
||||||
)
|
|
||||||
|
|
||||||
self.button_layout = QHBoxLayout()
|
|
||||||
self.column.addLayout(self.button_layout)
|
|
||||||
|
|
||||||
self.accept_button = QPushButton("Accept")
|
|
||||||
self.accept_button.clicked.connect(lambda state: self.accept())
|
|
||||||
self.accept_button.setEnabled(False)
|
|
||||||
self.button_layout.addWidget(self.accept_button)
|
|
||||||
|
|
||||||
self.cancel_button = QPushButton("Cancel")
|
|
||||||
self.cancel_button.clicked.connect(lambda state: self.cancel())
|
|
||||||
self.button_layout.addWidget(self.cancel_button)
|
|
||||||
|
|
||||||
def create_Squadron(
|
|
||||||
self, aircraft_type: AircraftType, base: ControlPoint
|
|
||||||
) -> Squadron:
|
|
||||||
squadron_def = SquadronDefGenerator(self.coalition).generate_for_aircraft(
|
|
||||||
aircraft_type
|
|
||||||
)
|
|
||||||
squadron = Squadron(
|
|
||||||
squadron_def.name,
|
|
||||||
squadron_def.nickname,
|
|
||||||
squadron_def.country,
|
|
||||||
squadron_def.role,
|
|
||||||
squadron_def.aircraft,
|
|
||||||
squadron_def.livery,
|
|
||||||
squadron_def.mission_types,
|
|
||||||
squadron_def.operating_bases,
|
|
||||||
squadron_def.pilot_pool,
|
|
||||||
self.coalition,
|
|
||||||
self.game.settings,
|
|
||||||
base,
|
|
||||||
)
|
|
||||||
return squadron
|
|
||||||
|
|
||||||
def on_aircraft_selection(self, index: int) -> None:
|
|
||||||
aircraft_type_name = self.aircraft_type_selector.currentText()
|
|
||||||
aircraft_type = None
|
|
||||||
for aircraft in self.coalition.faction.aircrafts:
|
|
||||||
if str(aircraft) == aircraft_type_name:
|
|
||||||
aircraft_type = aircraft
|
|
||||||
|
|
||||||
if aircraft != None:
|
|
||||||
self.squadron = self.create_Squadron(
|
|
||||||
aircraft_type,
|
|
||||||
next(self.theater.control_points_for(self.coalition.player)),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.name_edit.setText(self.squadron.name)
|
|
||||||
self.name_edit.setEnabled(True)
|
|
||||||
|
|
||||||
self.nickname_edit.setText(self.squadron.nickname)
|
|
||||||
self.nickname_edit.setEnabled(True)
|
|
||||||
|
|
||||||
self.accept_button.setStyleSheet("background-color: green")
|
|
||||||
self.accept_button.setEnabled(True)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def on_name_changed(self, text: str) -> None:
|
|
||||||
self.squadron.name = text
|
|
||||||
|
|
||||||
def on_nickname_changed(self, text: str) -> None:
|
|
||||||
self.squadron.nickname = text
|
|
||||||
|
|
||||||
def accept(self) -> None:
|
|
||||||
self.coalition.air_wing.add_squadron(self.squadron)
|
|
||||||
return super().accept()
|
|
||||||
|
|
||||||
def cancel(self) -> None:
|
|
||||||
return super().reject()
|
|
||||||
BIN
resources/ui/misc/dark/reload.png
Normal file
BIN
resources/ui/misc/dark/reload.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
resources/ui/misc/light/reload.png
Normal file
BIN
resources/ui/misc/light/reload.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/ui/misc/medium/reload.png
Normal file
BIN
resources/ui/misc/medium/reload.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Loading…
x
Reference in New Issue
Block a user