diff --git a/game/campaignloader/campaignairwingconfig.py b/game/campaignloader/campaignairwingconfig.py index 89d0a664..fc5b4e6a 100644 --- a/game/campaignloader/campaignairwingconfig.py +++ b/game/campaignloader/campaignairwingconfig.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging from collections import defaultdict from dataclasses import dataclass -from typing import Any, TYPE_CHECKING, Union +from typing import Any, TYPE_CHECKING, Union, Optional from game.ato.flighttype import FlightType from game.theater.controlpoint import ControlPoint @@ -18,6 +18,10 @@ class SquadronConfig: secondary: list[FlightType] aircraft: list[str] + name: Optional[str] + nickname: Optional[str] + female_pilot_ratio: Optional[int] + @property def auto_assignable(self) -> set[FlightType]: return set(self.secondary) | {self.primary} @@ -33,7 +37,12 @@ class SquadronConfig: secondary = [FlightType(s) for s in secondary_raw] return SquadronConfig( - FlightType(data["primary"]), secondary, data.get("aircraft", []) + FlightType(data["primary"]), + secondary, + data.get("aircraft", []), + data.get("name", None), + data.get("nickname", None), + data.get("female_pilot_ratio", None), ) @staticmethod diff --git a/game/campaignloader/defaultsquadronassigner.py b/game/campaignloader/defaultsquadronassigner.py index 54cd66e5..883f1f57 100644 --- a/game/campaignloader/defaultsquadronassigner.py +++ b/game/campaignloader/defaultsquadronassigner.py @@ -1,5 +1,6 @@ from __future__ import annotations +import dataclasses import logging from typing import Optional, TYPE_CHECKING @@ -48,24 +49,41 @@ class DefaultSquadronAssigner: def find_squadron_for( self, config: SquadronConfig, control_point: ControlPoint ) -> Optional[SquadronDef]: + squadron_def = None for preferred_aircraft in config.aircraft: squadron_def = self.find_preferred_squadron( preferred_aircraft, config.primary, control_point ) if squadron_def is not None: - return squadron_def + break # If we didn't find any of the preferred types we should use any squadron # compatible with the primary task. - squadron_def = self.find_squadron_for_task(config.primary, control_point) - if squadron_def is not None: - return squadron_def + if squadron_def is None: + squadron_def = self.find_squadron_for_task(config.primary, control_point) # If we can't find any squadron matching the requirement, we should # create one. - return self.air_wing.squadron_def_generator.generate_for_task( - config.primary, control_point - ) + if squadron_def is None: + squadron_def = self.air_wing.squadron_def_generator.generate_for_task( + config.primary, control_point + ) + + # Override squadron def with squadron config parameters from campaign file, if defined + if squadron_def is not None: + + overrides = {} + if config.name is not None: + overrides["name"] = config.name + if config.nickname is not None: + overrides["nickname"] = config.nickname + if config.female_pilot_ratio is not None: + overrides["female_pilot_ratio"] = config.female_pilot_ratio + + squadron_copy = dataclasses.replace(squadron_def, **overrides) + return squadron_copy + else: + return None def find_preferred_squadron( self, preferred_aircraft: str, task: FlightType, control_point: ControlPoint diff --git a/game/campaignloader/squadrondefgenerator.py b/game/campaignloader/squadrondefgenerator.py index 82462910..6b8203d8 100644 --- a/game/campaignloader/squadrondefgenerator.py +++ b/game/campaignloader/squadrondefgenerator.py @@ -50,6 +50,7 @@ class SquadronDefGenerator: livery=None, mission_types=tuple(tasks_for_aircraft(aircraft)), operating_bases=OperatingBases.default_for_aircraft(aircraft), + female_pilot_ratio=6, pilot_pool=[], ) diff --git a/game/squadrons/squadron.py b/game/squadrons/squadron.py index f2078bd9..b3f359c6 100644 --- a/game/squadrons/squadron.py +++ b/game/squadrons/squadron.py @@ -1,6 +1,7 @@ from __future__ import annotations import logging +import random from collections.abc import Iterable from dataclasses import dataclass, field from typing import Optional, Sequence, TYPE_CHECKING @@ -32,6 +33,7 @@ class Squadron: livery: Optional[str] mission_types: tuple[FlightType, ...] operating_bases: OperatingBases + female_pilot_ratio: int #: The pool of pilots that have not yet been assigned to the squadron. This only #: happens when a preset squadron defines more preset pilots than the squadron limit @@ -159,7 +161,11 @@ class Squadron: new_pilots = self.pilot_pool[:count] self.pilot_pool = self.pilot_pool[count:] count -= len(new_pilots) - new_pilots.extend([Pilot(self.faker.name()) for _ in range(count)]) + for _ in range(count): + if random.randint(1, 100) > self.female_pilot_ratio: + new_pilots.append(Pilot(self.faker.name_male())) + else: + new_pilots.append(Pilot(self.faker.name_female())) self.current_roster.extend(new_pilots) self.available_pilots.extend(new_pilots) @@ -428,6 +434,7 @@ class Squadron: squadron_def.livery, squadron_def.mission_types, squadron_def.operating_bases, + squadron_def.female_pilot_ratio, squadron_def.pilot_pool, coalition, game.settings, diff --git a/game/squadrons/squadrondef.py b/game/squadrons/squadrondef.py index 6ed28815..f74fdabb 100644 --- a/game/squadrons/squadrondef.py +++ b/game/squadrons/squadrondef.py @@ -27,6 +27,7 @@ class SquadronDef: livery: Optional[str] mission_types: tuple[FlightType, ...] operating_bases: OperatingBases + female_pilot_ratio: int pilot_pool: list[Pilot] claimed: bool = False @@ -75,6 +76,7 @@ class SquadronDef: pilots = [Pilot(n, player=False) for n in data.get("pilots", [])] pilots.extend([Pilot(n, player=True) for n in data.get("players", [])]) + female_pilot_ratio = data.get("female_pilot_ratio", 6) mission_types = [FlightType.from_name(n) for n in data["mission_types"]] tasks = tasks_for_aircraft(unit_type) @@ -95,5 +97,6 @@ class SquadronDef: livery=data.get("livery"), mission_types=tuple(mission_types), operating_bases=OperatingBases.from_yaml(unit_type, data.get("bases", {})), + female_pilot_ratio=female_pilot_ratio, pilot_pool=pilots, )