mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Disallow squadrons from disabling mission types.
After this change, players will always have the final say in what missions a squadron can be assigned to. Squadrons are not able to influence the default auto-assignable missions either because that property is always overridden by the campaign's air wing configuration (the primary and secondary task properties). The `mission-types` field of the squadron definition has been removed since it is no longer capable of influencing anything. I haven't bothered cleaning up the now useless data in all the existing squadrons though. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2785.
This commit is contained in:
parent
1ac36d03da
commit
94b8aa7213
@ -14,6 +14,8 @@ Saves from 6.x are not compatible with 7.0.
|
|||||||
* **[Modding]** Add support for VSN F-4B and F-4C mod.
|
* **[Modding]** Add support for VSN F-4B and F-4C mod.
|
||||||
* **[Modding]** Custom factions can now be defined in YAML as well as JSON. JSON support may be removed in the future if having both formats causes confusion.
|
* **[Modding]** Custom factions can now be defined in YAML as well as JSON. JSON support may be removed in the future if having both formats causes confusion.
|
||||||
* **[Modding]** Campaigns which require custom factions can now define those factions directly in the campaign YAML. See Operation Aliied Sword for an example.
|
* **[Modding]** Campaigns which require custom factions can now define those factions directly in the campaign YAML. See Operation Aliied Sword for an example.
|
||||||
|
* **[Modding]** The `mission_types` field in squadron files has been removed. Squadron task capability is now determined by airframe, and the auto-assignable list has always been overridden by the campaign settings.
|
||||||
|
* **[Squadrons]** Squadron-specific mission capability lists no longer restrict players from assigning missions outside the squadron's preferences.
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
|
||||||
|
|||||||
@ -115,7 +115,7 @@ class DefaultSquadronAssigner:
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
if ignore_base_preference:
|
if ignore_base_preference:
|
||||||
return control_point.can_operate(squadron.aircraft)
|
return control_point.can_operate(squadron.aircraft)
|
||||||
return squadron.operates_from(control_point) and task in squadron.mission_types
|
return squadron.operates_from(control_point) and squadron.capable_of(task)
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import itertools
|
|||||||
import random
|
import random
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
|
from game.ato.ai_flight_planner_db import aircraft_for_task, tasks_for_aircraft
|
||||||
from game.ato.flighttype import FlightType
|
from game.ato.flighttype import FlightType
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
from game.squadrons.operatingbases import OperatingBases
|
from game.squadrons.operatingbases import OperatingBases
|
||||||
from game.squadrons.squadrondef import SquadronDef
|
from game.squadrons.squadrondef import SquadronDef
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from game.ato.ai_flight_planner_db import aircraft_for_task, tasks_for_aircraft
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.factions.faction import Faction
|
from game.factions.faction import Faction
|
||||||
@ -48,7 +48,7 @@ class SquadronDefGenerator:
|
|||||||
role="Flying Squadron",
|
role="Flying Squadron",
|
||||||
aircraft=aircraft,
|
aircraft=aircraft,
|
||||||
livery=None,
|
livery=None,
|
||||||
mission_types=tuple(tasks_for_aircraft(aircraft)),
|
auto_assignable_mission_types=set(tasks_for_aircraft(aircraft)),
|
||||||
operating_bases=OperatingBases.default_for_aircraft(aircraft),
|
operating_bases=OperatingBases.default_for_aircraft(aircraft),
|
||||||
female_pilot_percentage=6,
|
female_pilot_percentage=6,
|
||||||
pilot_pool=[],
|
pilot_pool=[],
|
||||||
|
|||||||
@ -2,11 +2,11 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Sequence, Iterator, TYPE_CHECKING, Optional
|
from typing import Iterator, Optional, Sequence, TYPE_CHECKING
|
||||||
|
|
||||||
from game.dcs.aircrafttype import AircraftType
|
|
||||||
from game.ato.ai_flight_planner_db import aircraft_for_task
|
from game.ato.ai_flight_planner_db import aircraft_for_task
|
||||||
from game.ato.closestairfields import ObjectiveDistanceCache
|
from game.ato.closestairfields import ObjectiveDistanceCache
|
||||||
|
from game.dcs.aircrafttype import AircraftType
|
||||||
from .squadrondefloader import SquadronDefLoader
|
from .squadrondefloader import SquadronDefLoader
|
||||||
from ..campaignloader.squadrondefgenerator import SquadronDefGenerator
|
from ..campaignloader.squadrondefgenerator import SquadronDefGenerator
|
||||||
from ..factions.faction import Faction
|
from ..factions.faction import Faction
|
||||||
@ -82,7 +82,7 @@ class AirWing:
|
|||||||
best_aircraft_for_task = aircraft_for_task(task)
|
best_aircraft_for_task = aircraft_for_task(task)
|
||||||
for aircraft, squadrons in self.squadrons.items():
|
for aircraft, squadrons in self.squadrons.items():
|
||||||
for squadron in squadrons:
|
for squadron in squadrons:
|
||||||
if squadron.untasked_aircraft and task in squadron.mission_types:
|
if squadron.untasked_aircraft and squadron.capable_of(task):
|
||||||
aircrafts.append(aircraft)
|
aircrafts.append(aircraft)
|
||||||
if aircraft not in best_aircraft_for_task:
|
if aircraft not in best_aircraft_for_task:
|
||||||
best_aircraft_for_task.append(aircraft)
|
best_aircraft_for_task.append(aircraft)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from faker import Faker
|
|||||||
from game.ato import Flight, FlightType, Package
|
from game.ato import Flight, FlightType, Package
|
||||||
from game.settings import AutoAtoBehavior, Settings
|
from game.settings import AutoAtoBehavior, Settings
|
||||||
from .pilot import Pilot, PilotStatus
|
from .pilot import Pilot, PilotStatus
|
||||||
|
from ..ato.ai_flight_planner_db import aircraft_for_task
|
||||||
from ..db.database import Database
|
from ..db.database import Database
|
||||||
from ..utils import meters
|
from ..utils import meters
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ class Squadron:
|
|||||||
role: str
|
role: str
|
||||||
aircraft: AircraftType
|
aircraft: AircraftType
|
||||||
livery: Optional[str]
|
livery: Optional[str]
|
||||||
mission_types: tuple[FlightType, ...]
|
auto_assignable_mission_types: set[FlightType]
|
||||||
operating_bases: OperatingBases
|
operating_bases: OperatingBases
|
||||||
female_pilot_percentage: int
|
female_pilot_percentage: int
|
||||||
|
|
||||||
@ -46,10 +47,6 @@ class Squadron:
|
|||||||
default_factory=list, init=False, hash=False, compare=False
|
default_factory=list, init=False, hash=False, compare=False
|
||||||
)
|
)
|
||||||
|
|
||||||
auto_assignable_mission_types: set[FlightType] = field(
|
|
||||||
init=False, hash=False, compare=False
|
|
||||||
)
|
|
||||||
|
|
||||||
coalition: Coalition = field(hash=False, compare=False)
|
coalition: Coalition = field(hash=False, compare=False)
|
||||||
flight_db: Database[Flight] = field(hash=False, compare=False)
|
flight_db: Database[Flight] = field(hash=False, compare=False)
|
||||||
settings: Settings = field(hash=False, compare=False)
|
settings: Settings = field(hash=False, compare=False)
|
||||||
@ -63,9 +60,6 @@ class Squadron:
|
|||||||
untasked_aircraft: int = field(init=False, hash=False, compare=False, default=0)
|
untasked_aircraft: int = field(init=False, hash=False, compare=False, default=0)
|
||||||
pending_deliveries: int = field(init=False, hash=False, compare=False, default=0)
|
pending_deliveries: int = field(init=False, hash=False, compare=False, default=0)
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
|
||||||
self.auto_assignable_mission_types = set(self.mission_types)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.nickname is None:
|
if self.nickname is None:
|
||||||
return self.name
|
return self.name
|
||||||
@ -94,16 +88,12 @@ class Squadron:
|
|||||||
def pilot_limits_enabled(self) -> bool:
|
def pilot_limits_enabled(self) -> bool:
|
||||||
return self.settings.enable_squadron_pilot_limits
|
return self.settings.enable_squadron_pilot_limits
|
||||||
|
|
||||||
def set_allowed_mission_types(self, mission_types: Iterable[FlightType]) -> None:
|
|
||||||
self.mission_types = tuple(mission_types)
|
|
||||||
self.auto_assignable_mission_types.intersection_update(self.mission_types)
|
|
||||||
|
|
||||||
def set_auto_assignable_mission_types(
|
def set_auto_assignable_mission_types(
|
||||||
self, mission_types: Iterable[FlightType]
|
self, mission_types: Iterable[FlightType]
|
||||||
) -> None:
|
) -> None:
|
||||||
self.auto_assignable_mission_types = set(self.mission_types).intersection(
|
self.auto_assignable_mission_types = {
|
||||||
mission_types
|
t for t in mission_types if self.capable_of(t)
|
||||||
)
|
}
|
||||||
|
|
||||||
def claim_new_pilot_if_allowed(self) -> Optional[Pilot]:
|
def claim_new_pilot_if_allowed(self) -> Optional[Pilot]:
|
||||||
if self.pilot_limits_enabled:
|
if self.pilot_limits_enabled:
|
||||||
@ -257,7 +247,20 @@ class Squadron:
|
|||||||
def has_unfilled_pilot_slots(self) -> bool:
|
def has_unfilled_pilot_slots(self) -> bool:
|
||||||
return not self.pilot_limits_enabled or self._number_of_unfilled_pilot_slots > 0
|
return not self.pilot_limits_enabled or self._number_of_unfilled_pilot_slots > 0
|
||||||
|
|
||||||
|
def capable_of(self, task: FlightType) -> bool:
|
||||||
|
"""Returns True if the squadron is capable of performing the given task.
|
||||||
|
|
||||||
|
A squadron may be capable of performing a task even if it will not be
|
||||||
|
automatically assigned to it.
|
||||||
|
"""
|
||||||
|
return self.aircraft in aircraft_for_task(task)
|
||||||
|
|
||||||
def can_auto_assign(self, task: FlightType) -> bool:
|
def can_auto_assign(self, task: FlightType) -> bool:
|
||||||
|
"""Returns True if the squadron may be automatically assigned the given task.
|
||||||
|
|
||||||
|
A squadron may be capable of performing a task even if it will not be
|
||||||
|
automatically assigned to it.
|
||||||
|
"""
|
||||||
return task in self.auto_assignable_mission_types
|
return task in self.auto_assignable_mission_types
|
||||||
|
|
||||||
def can_auto_assign_mission(
|
def can_auto_assign_mission(
|
||||||
@ -432,7 +435,7 @@ class Squadron:
|
|||||||
squadron_def.role,
|
squadron_def.role,
|
||||||
squadron_def.aircraft,
|
squadron_def.aircraft,
|
||||||
squadron_def.livery,
|
squadron_def.livery,
|
||||||
squadron_def.mission_types,
|
squadron_def.auto_assignable_mission_types,
|
||||||
squadron_def.operating_bases,
|
squadron_def.operating_bases,
|
||||||
squadron_def.female_pilot_percentage,
|
squadron_def.female_pilot_percentage,
|
||||||
squadron_def.pilot_pool,
|
squadron_def.pilot_pool,
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
from dataclasses import dataclass
|
||||||
from collections.abc import Iterable
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
from game.ato.ai_flight_planner_db import aircraft_for_task, tasks_for_aircraft
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
from game.squadrons.operatingbases import OperatingBases
|
from game.squadrons.operatingbases import OperatingBases
|
||||||
from game.squadrons.pilot import Pilot
|
from game.squadrons.pilot import Pilot
|
||||||
@ -25,30 +24,24 @@ class SquadronDef:
|
|||||||
role: str
|
role: str
|
||||||
aircraft: AircraftType
|
aircraft: AircraftType
|
||||||
livery: Optional[str]
|
livery: Optional[str]
|
||||||
mission_types: tuple[FlightType, ...]
|
auto_assignable_mission_types: set[FlightType]
|
||||||
operating_bases: OperatingBases
|
operating_bases: OperatingBases
|
||||||
female_pilot_percentage: int
|
female_pilot_percentage: int
|
||||||
pilot_pool: list[Pilot]
|
pilot_pool: list[Pilot]
|
||||||
claimed: bool = False
|
claimed: bool = False
|
||||||
|
|
||||||
auto_assignable_mission_types: set[FlightType] = field(
|
|
||||||
init=False, hash=False, compare=False
|
|
||||||
)
|
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
|
||||||
self.auto_assignable_mission_types = set(self.mission_types)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.nickname is None:
|
if self.nickname is None:
|
||||||
return self.name
|
return self.name
|
||||||
return f'{self.name} "{self.nickname}"'
|
return f'{self.name} "{self.nickname}"'
|
||||||
|
|
||||||
def set_allowed_mission_types(self, mission_types: Iterable[FlightType]) -> None:
|
def capable_of(self, task: FlightType) -> bool:
|
||||||
self.mission_types = tuple(mission_types)
|
"""Returns True if the squadron is capable of performing the given task.
|
||||||
self.auto_assignable_mission_types.intersection_update(self.mission_types)
|
|
||||||
|
|
||||||
def can_auto_assign(self, task: FlightType) -> bool:
|
A squadron may be capable of performing a task even if it will not be
|
||||||
return task in self.auto_assignable_mission_types
|
automatically assigned to it.
|
||||||
|
"""
|
||||||
|
return self.aircraft in aircraft_for_task(task)
|
||||||
|
|
||||||
def operates_from(self, control_point: ControlPoint) -> bool:
|
def operates_from(self, control_point: ControlPoint) -> bool:
|
||||||
if not control_point.can_operate(self.aircraft):
|
if not control_point.can_operate(self.aircraft):
|
||||||
@ -62,8 +55,6 @@ class SquadronDef:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_yaml(cls, path: Path) -> SquadronDef:
|
def from_yaml(cls, path: Path) -> SquadronDef:
|
||||||
from game.ato.ai_flight_planner_db import tasks_for_aircraft
|
|
||||||
from game.ato import FlightType
|
|
||||||
|
|
||||||
with path.open(encoding="utf8") as squadron_file:
|
with path.open(encoding="utf8") as squadron_file:
|
||||||
data = yaml.safe_load(squadron_file)
|
data = yaml.safe_load(squadron_file)
|
||||||
@ -78,16 +69,6 @@ class SquadronDef:
|
|||||||
pilots.extend([Pilot(n, player=True) for n in data.get("players", [])])
|
pilots.extend([Pilot(n, player=True) for n in data.get("players", [])])
|
||||||
female_pilot_percentage = data.get("female_pilot_percentage", 6)
|
female_pilot_percentage = data.get("female_pilot_percentage", 6)
|
||||||
|
|
||||||
mission_types = [FlightType.from_name(n) for n in data["mission_types"]]
|
|
||||||
tasks = tasks_for_aircraft(unit_type)
|
|
||||||
for mission_type in list(mission_types):
|
|
||||||
if mission_type not in tasks:
|
|
||||||
logging.error(
|
|
||||||
f"Squadron has mission type {mission_type} but {unit_type} is not "
|
|
||||||
f"capable of that task: {path}"
|
|
||||||
)
|
|
||||||
mission_types.remove(mission_type)
|
|
||||||
|
|
||||||
return SquadronDef(
|
return SquadronDef(
|
||||||
name=data["name"],
|
name=data["name"],
|
||||||
nickname=data.get("nickname"),
|
nickname=data.get("nickname"),
|
||||||
@ -95,7 +76,7 @@ class SquadronDef:
|
|||||||
role=data["role"],
|
role=data["role"],
|
||||||
aircraft=unit_type,
|
aircraft=unit_type,
|
||||||
livery=data.get("livery"),
|
livery=data.get("livery"),
|
||||||
mission_types=tuple(mission_types),
|
auto_assignable_mission_types=set(tasks_for_aircraft(unit_type)),
|
||||||
operating_bases=OperatingBases.from_yaml(unit_type, data.get("bases", {})),
|
operating_bases=OperatingBases.from_yaml(unit_type, data.get("bases", {})),
|
||||||
female_pilot_percentage=female_pilot_percentage,
|
female_pilot_percentage=female_pilot_percentage,
|
||||||
pilot_pool=pilots,
|
pilot_pool=pilots,
|
||||||
|
|||||||
@ -44,9 +44,6 @@ class QMissionType:
|
|||||||
self, mission_type: FlightType, allowed: bool, auto_assignable: bool
|
self, mission_type: FlightType, allowed: bool, auto_assignable: bool
|
||||||
) -> None:
|
) -> None:
|
||||||
self.flight_type = mission_type
|
self.flight_type = mission_type
|
||||||
self.allowed_checkbox = QCheckBox()
|
|
||||||
self.allowed_checkbox.setChecked(allowed)
|
|
||||||
self.allowed_checkbox.toggled.connect(self.update_auto_assignable)
|
|
||||||
self.auto_assignable_checkbox = QCheckBox()
|
self.auto_assignable_checkbox = QCheckBox()
|
||||||
self.auto_assignable_checkbox.setEnabled(allowed)
|
self.auto_assignable_checkbox.setEnabled(allowed)
|
||||||
self.auto_assignable_checkbox.setChecked(auto_assignable)
|
self.auto_assignable_checkbox.setChecked(auto_assignable)
|
||||||
@ -56,10 +53,6 @@ class QMissionType:
|
|||||||
if not checked:
|
if not checked:
|
||||||
self.auto_assignable_checkbox.setChecked(False)
|
self.auto_assignable_checkbox.setChecked(False)
|
||||||
|
|
||||||
@property
|
|
||||||
def allowed(self) -> bool:
|
|
||||||
return self.allowed_checkbox.isChecked()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def auto_assignable(self) -> bool:
|
def auto_assignable(self) -> bool:
|
||||||
return self.auto_assignable_checkbox.isChecked()
|
return self.auto_assignable_checkbox.isChecked()
|
||||||
@ -72,27 +65,20 @@ class MissionTypeControls(QGridLayout):
|
|||||||
self.mission_types: list[QMissionType] = []
|
self.mission_types: list[QMissionType] = []
|
||||||
|
|
||||||
self.addWidget(QLabel("Mission Type"), 0, 0)
|
self.addWidget(QLabel("Mission Type"), 0, 0)
|
||||||
self.addWidget(QLabel("Allow"), 0, 1)
|
self.addWidget(QLabel("Auto-Assign"), 0, 1)
|
||||||
self.addWidget(QLabel("Auto-Assign"), 0, 2)
|
|
||||||
|
|
||||||
for i, task in enumerate(FlightType):
|
for i, task in enumerate(FlightType):
|
||||||
if task is FlightType.FERRY:
|
if task is FlightType.FERRY:
|
||||||
# Not plannable so just skip it.
|
# Not plannable so just skip it.
|
||||||
continue
|
continue
|
||||||
allowed = task in squadron.mission_types
|
|
||||||
auto_assignable = task in squadron.auto_assignable_mission_types
|
auto_assignable = task in squadron.auto_assignable_mission_types
|
||||||
mission_type = QMissionType(task, allowed, auto_assignable)
|
mission_type = QMissionType(
|
||||||
|
task, squadron.capable_of(task), auto_assignable
|
||||||
|
)
|
||||||
self.mission_types.append(mission_type)
|
self.mission_types.append(mission_type)
|
||||||
|
|
||||||
self.addWidget(QLabel(task.value), i + 1, 0)
|
self.addWidget(QLabel(task.value), i + 1, 0)
|
||||||
self.addWidget(mission_type.allowed_checkbox, i + 1, 1)
|
self.addWidget(mission_type.auto_assignable_checkbox, i + 1, 1)
|
||||||
self.addWidget(mission_type.auto_assignable_checkbox, i + 1, 2)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def allowed_mission_types(self) -> Iterator[FlightType]:
|
|
||||||
for mission_type in self.mission_types:
|
|
||||||
if mission_type.allowed:
|
|
||||||
yield mission_type.flight_type
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def auto_assignable_mission_types(self) -> Iterator[FlightType]:
|
def auto_assignable_mission_types(self) -> Iterator[FlightType]:
|
||||||
@ -233,10 +219,6 @@ class SquadronConfigurationBox(QGroupBox):
|
|||||||
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
|
||||||
# Set the allowed mission types
|
|
||||||
self.squadron.set_allowed_mission_types(
|
|
||||||
set(self.mission_types.allowed_mission_types)
|
|
||||||
)
|
|
||||||
# Also update the auto assignable mission types
|
# Also update the auto assignable mission types
|
||||||
self.squadron.set_auto_assignable_mission_types(
|
self.squadron.set_auto_assignable_mission_types(
|
||||||
set(self.mission_types.auto_assignable_mission_types)
|
set(self.mission_types.auto_assignable_mission_types)
|
||||||
|
|||||||
@ -77,7 +77,8 @@ class AutoAssignedTaskControls(QVBoxLayout):
|
|||||||
|
|
||||||
return callback
|
return callback
|
||||||
|
|
||||||
for task in squadron_model.squadron.mission_types:
|
for task in FlightType:
|
||||||
|
if self.squadron_model.squadron.capable_of(task):
|
||||||
checkbox = QCheckBox(text=task.value)
|
checkbox = QCheckBox(text=task.value)
|
||||||
checkbox.setChecked(squadron_model.is_auto_assignable(task))
|
checkbox.setChecked(squadron_model.is_auto_assignable(task))
|
||||||
checkbox.toggled.connect(make_callback(task))
|
checkbox.toggled.connect(make_callback(task))
|
||||||
|
|||||||
@ -48,7 +48,7 @@ class SquadronSelector(QComboBox):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for squadron in self.air_wing.squadrons_for(aircraft):
|
for squadron in self.air_wing.squadrons_for(aircraft):
|
||||||
if task in squadron.mission_types and squadron.untasked_aircraft:
|
if squadron.capable_of(task) and squadron.untasked_aircraft:
|
||||||
self.addItem(f"{squadron.location}: {squadron}", squadron)
|
self.addItem(f"{squadron.location}: {squadron}", squadron)
|
||||||
|
|
||||||
if self.count() == 0:
|
if self.count() == 0:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user