Add logic for specification of ground_units in campaign yaml file

Resolves #22
This commit is contained in:
Raffson 2023-02-12 21:24:21 +01:00
parent 34b9d8c334
commit d9967bda8f
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
7 changed files with 65 additions and 3 deletions

View File

@ -23,6 +23,7 @@
* **[UI]** Configurable LINK4 for Carriers. * **[UI]** Configurable LINK4 for Carriers.
* **[Kneeboard]** Show package information in Support page * **[Kneeboard]** Show package information in Support page
* **[Campaign Design]** Ability to define designated CTLD zones for Control Points (Airbases & FOBs/FARPs) * **[Campaign Design]** Ability to define designated CTLD zones for Control Points (Airbases & FOBs/FARPs)
* **[Campaign Design]** Ability to define preset groups for specific TGOs, given the preset group is accessible for the faction and the task matches.
## Fixes ## Fixes
* **[UI]** Removed deprecated options * **[UI]** Removed deprecated options

View File

@ -19,6 +19,7 @@ from game.theater.iadsnetwork.iadsnetwork import IadsNetwork
from game.theater.theaterloader import TheaterLoader from game.theater.theaterloader import TheaterLoader
from game.version import CAMPAIGN_FORMAT_VERSION from game.version import CAMPAIGN_FORMAT_VERSION
from .campaignairwingconfig import CampaignAirWingConfig from .campaignairwingconfig import CampaignAirWingConfig
from .campaigngroundconfig import TgoConfig
from .mizcampaignloader import MizCampaignLoader from .mizcampaignloader import MizCampaignLoader
PERF_FRIENDLY = 0 PERF_FRIENDLY = 0
@ -137,6 +138,13 @@ class Campaign:
return CampaignAirWingConfig({}) return CampaignAirWingConfig({})
return CampaignAirWingConfig.from_campaign_data(squadron_data, theater) return CampaignAirWingConfig.from_campaign_data(squadron_data, theater)
def load_ground_forces_config(self) -> TgoConfig:
ground_forces = self.data.get("ground_forces", {})
if not ground_forces:
logging.warning(f"Campaign {self.name} does not define any squadrons")
return TgoConfig({})
return TgoConfig.from_campaign_data(ground_forces)
@property @property
def is_out_of_date(self) -> bool: def is_out_of_date(self) -> bool:
"""Returns True if this campaign is not up to date with the latest format. """Returns True if this campaign is not up to date with the latest format.

View File

@ -0,0 +1,25 @@
from __future__ import annotations
from collections import defaultdict
from dataclasses import dataclass
from typing import Iterator, Optional
from game.armedforces.forcegroup import ForceGroup
@dataclass(frozen=True)
class TgoConfig:
by_original_name: dict[str, ForceGroup]
def __iter__(self) -> Iterator[str]:
return self.by_original_name.__iter__()
def __getitem__(self, name: str) -> Optional[ForceGroup]:
return self.by_original_name.get(name)
@classmethod
def from_campaign_data(cls, data: dict[str, str]) -> TgoConfig:
by_original_name: dict[str, ForceGroup] = defaultdict()
for tgo_name, force_group in data.items():
by_original_name[tgo_name] = ForceGroup.from_preset_group(force_group)
return TgoConfig(by_original_name)

View File

@ -4,7 +4,7 @@ import logging
import random import random
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, time from datetime import datetime, time
from typing import List from typing import List, Optional
import dcs.statics import dcs.statics
@ -30,6 +30,7 @@ from .theatergroup import IadsGroundGroup, IadsRole, SceneryUnit, TheaterGroup
from ..armedforces.armedforces import ArmedForces from ..armedforces.armedforces import ArmedForces
from ..armedforces.forcegroup import ForceGroup from ..armedforces.forcegroup import ForceGroup
from ..campaignloader.campaignairwingconfig import CampaignAirWingConfig from ..campaignloader.campaignairwingconfig import CampaignAirWingConfig
from ..campaignloader.campaigngroundconfig import TgoConfig
from ..data.groups import GroupTask from ..data.groups import GroupTask
from ..profiling import logged_duration from ..profiling import logged_duration
from ..settings import Settings from ..settings import Settings
@ -47,6 +48,7 @@ class GeneratorSettings:
no_lha: bool no_lha: bool
no_player_navy: bool no_player_navy: bool
no_enemy_navy: bool no_enemy_navy: bool
tgo_config: TgoConfig
@dataclass @dataclass
@ -295,9 +297,31 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
self.generate_missile_sites() self.generate_missile_sites()
self.generate_coastal_sites() self.generate_coastal_sites()
def get_unit_group_for_task(
self, position: PresetLocation, task: GroupTask
) -> Optional[ForceGroup]:
tgo_config = self.generator_settings.tgo_config
fg = tgo_config[position.original_name]
valid_fg = (
fg
and task in fg.tasks
and all([u in self.faction.accessible_units for u in fg.units])
)
if valid_fg:
unit_group = fg
assert fg
self.armed_forces.add_or_update_force_group(fg)
else:
if fg and not valid_fg:
logging.warning(
f"Override in ground_forces failed for {fg} at {position.original_name}"
)
unit_group = self.armed_forces.random_group_for_task(task)
return unit_group
def generate_armor_groups(self) -> None: def generate_armor_groups(self) -> None:
for position in self.control_point.preset_locations.armor_groups: for position in self.control_point.preset_locations.armor_groups:
unit_group = self.armed_forces.random_group_for_task(GroupTask.BASE_DEFENSE) unit_group = self.get_unit_group_for_task(position, GroupTask.BASE_DEFENSE)
if not unit_group: if not unit_group:
logging.error(f"{self.faction_name} has no ForceGroup for Armor") logging.error(f"{self.faction_name} has no ForceGroup for Armor")
return return
@ -351,7 +375,7 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
def generate_aa_at(self, location: PresetLocation, tasks: list[GroupTask]) -> None: def generate_aa_at(self, location: PresetLocation, tasks: list[GroupTask]) -> None:
for task in tasks: for task in tasks:
unit_group = self.armed_forces.random_group_for_task(task) unit_group = self.get_unit_group_for_task(location, task)
if unit_group: if unit_group:
# Only take next (smaller) aa_range when no template available for the # Only take next (smaller) aa_range when no template available for the
# most requested range. Otherwise break the loop and continue # most requested range. Otherwise break the loop and continue

View File

@ -176,5 +176,7 @@ VERSION = _build_version_string()
#: #:
#: Version 10.6 #: Version 10.6
#: * Designated CTLD zones for ControlPoints (Airbases & FOBs/FARPs) #: * Designated CTLD zones for ControlPoints (Airbases & FOBs/FARPs)
#: * '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) CAMPAIGN_FORMAT_VERSION = (10, 6)

View File

@ -318,6 +318,7 @@ def create_game(
no_lha=False, no_lha=False,
no_player_navy=False, no_player_navy=False,
no_enemy_navy=False, no_enemy_navy=False,
tgo_config=campaign.load_ground_forces_config(),
), ),
ModSettings( ModSettings(
a4_skyhawk=False, a4_skyhawk=False,

View File

@ -172,6 +172,7 @@ class NewGameWizard(QtWidgets.QWizard):
no_lha=self.field("no_lha"), no_lha=self.field("no_lha"),
no_player_navy=self.field("no_player_navy"), no_player_navy=self.field("no_player_navy"),
no_enemy_navy=self.field("no_enemy_navy"), no_enemy_navy=self.field("no_enemy_navy"),
tgo_config=campaign.load_ground_forces_config(),
) )
mod_settings = ModSettings( mod_settings = ModSettings(
a4_skyhawk=self.field("a4_skyhawk"), a4_skyhawk=self.field("a4_skyhawk"),