mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add option to limit squadron sizes and begin full.
Adding temporarily as an option to make sure it's not a terrible idea, but the old mode will probably go away. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1583. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2808.
This commit is contained in:
@@ -11,11 +11,15 @@ if TYPE_CHECKING:
|
||||
from game.theater import ConflictTheater
|
||||
|
||||
|
||||
DEFAULT_SQUADRON_SIZE = 12
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SquadronConfig:
|
||||
primary: FlightType
|
||||
secondary: list[FlightType]
|
||||
aircraft: list[str]
|
||||
max_size: int
|
||||
|
||||
name: Optional[str]
|
||||
nickname: Optional[str]
|
||||
@@ -39,6 +43,7 @@ class SquadronConfig:
|
||||
FlightType(data["primary"]),
|
||||
secondary,
|
||||
data.get("aircraft", []),
|
||||
data.get("size", DEFAULT_SQUADRON_SIZE),
|
||||
data.get("name", None),
|
||||
data.get("nickname", None),
|
||||
data.get("female_pilot_percentage", None),
|
||||
|
||||
@@ -44,6 +44,7 @@ class DefaultSquadronAssigner:
|
||||
squadron = Squadron.create_from(
|
||||
squadron_def,
|
||||
squadron_config.primary,
|
||||
squadron_config.max_size,
|
||||
control_point,
|
||||
self.coalition,
|
||||
self.game,
|
||||
|
||||
@@ -161,14 +161,14 @@ class Coalition:
|
||||
# is handled correctly.
|
||||
self.transfers.perform_transfers()
|
||||
|
||||
def preinit_turn_0(self) -> None:
|
||||
def preinit_turn_0(self, squadrons_start_full: bool) -> None:
|
||||
"""Runs final Coalition initialization.
|
||||
|
||||
Final initialization occurs before Game.initialize_turn runs for turn 0.
|
||||
"""
|
||||
self.air_wing.populate_for_turn_0()
|
||||
self.air_wing.populate_for_turn_0(squadrons_start_full)
|
||||
|
||||
def initialize_turn(self) -> None:
|
||||
def initialize_turn(self, is_turn_0: bool) -> None:
|
||||
"""Processes coalition-specific turn initialization.
|
||||
|
||||
For more information on turn initialization in general, see the documentation
|
||||
@@ -187,7 +187,8 @@ class Coalition:
|
||||
with logged_duration("Transport planning"):
|
||||
self.transfers.plan_transports()
|
||||
|
||||
self.plan_missions()
|
||||
if not is_turn_0 or not self.game.settings.enable_squadron_aircraft_limits:
|
||||
self.plan_missions()
|
||||
self.plan_procurement()
|
||||
|
||||
def refund_outstanding_orders(self) -> None:
|
||||
|
||||
16
game/game.py
16
game/game.py
@@ -294,7 +294,7 @@ class Game:
|
||||
if self.turn > 1:
|
||||
self.conditions = self.generate_conditions()
|
||||
|
||||
def begin_turn_0(self) -> None:
|
||||
def begin_turn_0(self, squadrons_start_full: bool) -> None:
|
||||
"""Initialization for the first turn of the game."""
|
||||
from .sim import GameUpdateEvents
|
||||
|
||||
@@ -319,8 +319,9 @@ class Game:
|
||||
# Rotate the whole TGO with the new heading
|
||||
tgo.rotate(heading or tgo.heading)
|
||||
|
||||
self.blue.preinit_turn_0()
|
||||
self.red.preinit_turn_0()
|
||||
self.blue.preinit_turn_0(squadrons_start_full)
|
||||
self.red.preinit_turn_0(squadrons_start_full)
|
||||
# TODO: Check for overfull bases.
|
||||
# We don't need to actually stream events for turn zero because we haven't given
|
||||
# *any* state to the UI yet, so it will need to do a full draw once we do.
|
||||
self.initialize_turn(GameUpdateEvents())
|
||||
@@ -365,7 +366,10 @@ class Game:
|
||||
self.red.bullseye = Bullseye(player_cp.position)
|
||||
|
||||
def initialize_turn(
|
||||
self, events: GameUpdateEvents, for_red: bool = True, for_blue: bool = True
|
||||
self,
|
||||
events: GameUpdateEvents,
|
||||
for_red: bool = True,
|
||||
for_blue: bool = True,
|
||||
) -> None:
|
||||
"""Performs turn initialization for the specified players.
|
||||
|
||||
@@ -418,9 +422,9 @@ class Game:
|
||||
|
||||
# Plan Coalition specific turn
|
||||
if for_blue:
|
||||
self.blue.initialize_turn()
|
||||
self.blue.initialize_turn(self.turn == 0)
|
||||
if for_red:
|
||||
self.red.initialize_turn()
|
||||
self.red.initialize_turn(self.turn == 0)
|
||||
|
||||
# Plan GroundWar
|
||||
self.ground_planners = {}
|
||||
|
||||
@@ -234,6 +234,8 @@ class ProcurementAi:
|
||||
):
|
||||
if not squadron.can_provide_pilots(request.number):
|
||||
continue
|
||||
if not squadron.has_aircraft_capacity_for(request.number):
|
||||
continue
|
||||
if squadron.location.unclaimed_parking() < request.number:
|
||||
continue
|
||||
if self.threat_zones.threatened(squadron.location.position):
|
||||
|
||||
@@ -109,7 +109,11 @@ class AircraftPurchaseAdapter(PurchaseAdapter[Squadron]):
|
||||
return item.owned_aircraft
|
||||
|
||||
def can_buy(self, item: Squadron) -> bool:
|
||||
return super().can_buy(item) and self.control_point.unclaimed_parking() > 0
|
||||
return (
|
||||
super().can_buy(item)
|
||||
and self.control_point.unclaimed_parking() > 0
|
||||
and item.has_aircraft_capacity_for(1)
|
||||
)
|
||||
|
||||
def can_sell(self, item: Squadron) -> bool:
|
||||
return item.untasked_aircraft > 0
|
||||
|
||||
@@ -245,6 +245,15 @@ class Settings:
|
||||
"this many pilots each turn up to the limit."
|
||||
),
|
||||
)
|
||||
# Feature flag for squadron limits.
|
||||
enable_squadron_aircraft_limits: bool = boolean_option(
|
||||
"Enable per-squadron aircraft limits",
|
||||
CAMPAIGN_MANAGEMENT_PAGE,
|
||||
PILOTS_AND_SQUADRONS_SECTION,
|
||||
default=False,
|
||||
remember_player_choice=True,
|
||||
detail="If set, squadrons will be limited to a maximum number of aircraft.",
|
||||
)
|
||||
|
||||
# HQ Automation
|
||||
automate_runway_repair: bool = boolean_option(
|
||||
|
||||
@@ -125,9 +125,9 @@ class AirWing:
|
||||
def squadron_at_index(self, index: int) -> Squadron:
|
||||
return list(self.iter_squadrons())[index]
|
||||
|
||||
def populate_for_turn_0(self) -> None:
|
||||
def populate_for_turn_0(self, squadrons_start_full: bool) -> None:
|
||||
for squadron in self.iter_squadrons():
|
||||
squadron.populate_for_turn_0()
|
||||
squadron.populate_for_turn_0(squadrons_start_full)
|
||||
|
||||
def end_turn(self) -> None:
|
||||
for squadron in self.iter_squadrons():
|
||||
|
||||
@@ -30,6 +30,7 @@ class Squadron:
|
||||
country: str
|
||||
role: str
|
||||
aircraft: AircraftType
|
||||
max_size: int
|
||||
livery: Optional[str]
|
||||
primary_task: FlightType
|
||||
auto_assignable_mission_types: set[FlightType]
|
||||
@@ -160,10 +161,12 @@ class Squadron:
|
||||
self.current_roster.extend(new_pilots)
|
||||
self.available_pilots.extend(new_pilots)
|
||||
|
||||
def populate_for_turn_0(self) -> None:
|
||||
def populate_for_turn_0(self, squadrons_start_full: bool) -> None:
|
||||
if any(p.status is not PilotStatus.Active for p in self.pilot_pool):
|
||||
raise ValueError("Squadrons can only be created with active pilots.")
|
||||
self._recruit_pilots(self.settings.squadron_pilot_limit)
|
||||
if squadrons_start_full:
|
||||
self.owned_aircraft = self.max_size
|
||||
|
||||
def end_turn(self) -> None:
|
||||
if self.destination is not None:
|
||||
@@ -201,7 +204,7 @@ class Squadron:
|
||||
return [p for p in self.current_roster if p.status != status]
|
||||
|
||||
@property
|
||||
def max_size(self) -> int:
|
||||
def pilot_limit(self) -> int:
|
||||
return self.settings.squadron_pilot_limit
|
||||
|
||||
@property
|
||||
@@ -229,7 +232,7 @@ class Squadron:
|
||||
|
||||
@property
|
||||
def _number_of_unfilled_pilot_slots(self) -> int:
|
||||
return self.max_size - len(self.active_pilots)
|
||||
return self.pilot_limit - len(self.active_pilots)
|
||||
|
||||
@property
|
||||
def number_of_available_pilots(self) -> int:
|
||||
@@ -328,6 +331,12 @@ class Squadron:
|
||||
def expected_size_next_turn(self) -> int:
|
||||
return self.owned_aircraft + self.pending_deliveries
|
||||
|
||||
def has_aircraft_capacity_for(self, n: int) -> bool:
|
||||
if not self.settings.enable_squadron_aircraft_limits:
|
||||
return True
|
||||
remaining = self.max_size - self.owned_aircraft - self.pending_deliveries
|
||||
return remaining >= n
|
||||
|
||||
@property
|
||||
def arrival(self) -> ControlPoint:
|
||||
return self.location if self.destination is None else self.destination
|
||||
@@ -418,6 +427,7 @@ class Squadron:
|
||||
cls,
|
||||
squadron_def: SquadronDef,
|
||||
primary_task: FlightType,
|
||||
max_size: int,
|
||||
base: ControlPoint,
|
||||
coalition: Coalition,
|
||||
game: Game,
|
||||
@@ -429,6 +439,7 @@ class Squadron:
|
||||
squadron_def.country,
|
||||
squadron_def.role,
|
||||
squadron_def.aircraft,
|
||||
max_size,
|
||||
squadron_def.livery,
|
||||
primary_task,
|
||||
squadron_def.auto_assignable_mission_types,
|
||||
|
||||
@@ -177,7 +177,6 @@ VERSION = _build_version_string()
|
||||
#:
|
||||
#: Version 10.6
|
||||
#: * Designated CTLD zones for ControlPoints (Airbases & FOBs/FARPs)
|
||||
#: * Support for defining squadron sizes.
|
||||
#: * 'ground_forces' in yaml file to specify preset groups for TGOs,
|
||||
#: given the group is available for the faction and the task matches
|
||||
|
||||
CAMPAIGN_FORMAT_VERSION = (10, 6)
|
||||
|
||||
Reference in New Issue
Block a user