mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Initial attempt
This commit is contained in:
@@ -19,6 +19,7 @@ from game.theater.iadsnetwork.iadsnetwork import IadsNetwork
|
||||
from game.theater.theaterloader import TheaterLoader
|
||||
from game.version import CAMPAIGN_FORMAT_VERSION
|
||||
from .campaignairwingconfig import CampaignAirWingConfig
|
||||
from .campaigncarrierconfig import CampaignCarrierConfig
|
||||
from .campaigngroundconfig import TgoConfig
|
||||
from .mizcampaignloader import MizCampaignLoader
|
||||
|
||||
@@ -140,6 +141,13 @@ class Campaign:
|
||||
return CampaignAirWingConfig({})
|
||||
return CampaignAirWingConfig.from_campaign_data(squadron_data, theater)
|
||||
|
||||
def load_carrier_config(self) -> CampaignCarrierConfig:
|
||||
try:
|
||||
carrier_data = self.data["carriers"]
|
||||
except KeyError:
|
||||
return CampaignCarrierConfig({})
|
||||
return CampaignCarrierConfig.from_campaign_data(carrier_data)
|
||||
|
||||
def load_ground_forces_config(self) -> TgoConfig:
|
||||
ground_forces = self.data.get("ground_forces", {})
|
||||
if not ground_forces:
|
||||
|
||||
42
game/campaignloader/campaigncarrierconfig.py
Normal file
42
game/campaignloader/campaigncarrierconfig.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, TYPE_CHECKING
|
||||
|
||||
from game.dcs.shipunittype import ShipUnitType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CarrierConfig:
|
||||
preferred_name: str
|
||||
preferred_type: ShipUnitType
|
||||
|
||||
@classmethod
|
||||
def from_data(cls, data: dict[str, Any]) -> CarrierConfig:
|
||||
return CarrierConfig(
|
||||
str(data["preferred_name"]), ShipUnitType.named(data["preferred_type"])
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CampaignCarrierConfig:
|
||||
by_original_name: dict[str, CarrierConfig]
|
||||
|
||||
@classmethod
|
||||
def from_campaign_data(
|
||||
cls, data: dict[str, Any]
|
||||
) -> CampaignCarrierConfig:
|
||||
by_original_name: dict[str, CarrierConfig] = defaultdict()
|
||||
for original_name, carrier_config_data in data.items():
|
||||
try:
|
||||
carrier_config = CarrierConfig.from_data(carrier_config_data)
|
||||
by_original_name[original_name] = carrier_config
|
||||
except KeyError:
|
||||
logging.warning(f"Skipping invalid carrier config for '{original_name}'")
|
||||
|
||||
return CampaignCarrierConfig(by_original_name)
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
from functools import cached_property
|
||||
from typing import Optional, Dict, Type, List, Any, Iterator, TYPE_CHECKING, Set
|
||||
@@ -93,8 +94,8 @@ class Faction:
|
||||
# Required mods or asset packs
|
||||
requirements: Dict[str, str] = field(default_factory=dict)
|
||||
|
||||
# Possible carrier names
|
||||
carrier_names: Set[str] = field(default_factory=set)
|
||||
# Possible aircraft carrier units
|
||||
carriers: Dict[ShipUnitType, Set[str]] = field(default_factory=dict)
|
||||
|
||||
# Possible helicopter carrier names
|
||||
helicopter_carrier_names: Set[str] = field(default_factory=set)
|
||||
@@ -241,8 +242,29 @@ class Faction:
|
||||
|
||||
faction.requirements = json.get("requirements", {})
|
||||
|
||||
faction.carrier_names = json.get("carrier_names", [])
|
||||
faction.helicopter_carrier_names = json.get("helicopter_carrier_names", [])
|
||||
# First try to load the carriers in the new format which
|
||||
# specifies different names for different carrier types
|
||||
loaded_carriers = load_carriers(json)
|
||||
|
||||
carriers: List[ShipUnitType] = [
|
||||
unit
|
||||
for unit in faction.naval_units
|
||||
if unit.unit_class
|
||||
in [
|
||||
UnitClass.AIRCRAFT_CARRIER,
|
||||
UnitClass.HELICOPTER_CARRIER,
|
||||
]
|
||||
]
|
||||
carrier_names = json.get("carrier_names", [])
|
||||
for c in carriers:
|
||||
if c.variant_id not in loaded_carriers:
|
||||
if c.unit_class == UnitClass.AIRCRAFT_CARRIER:
|
||||
loaded_carriers[c] = carrier_names
|
||||
elif c.unit_class == UnitClass.HELICOPTER_CARRIER:
|
||||
loaded_carriers[c] = faction.helicopter_carrier_names
|
||||
|
||||
faction.carriers = loaded_carriers
|
||||
faction.naval_units.union(faction.carriers.keys())
|
||||
|
||||
faction.has_jtac = json.get("has_jtac", False)
|
||||
jtac_name = json.get("jtac_unit", None)
|
||||
@@ -592,3 +614,14 @@ def load_all_ships(data: list[str]) -> List[Type[ShipType]]:
|
||||
if item is not None:
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
|
||||
def load_carriers(json: Dict[str, Any]) -> Dict[ShipUnitType, Set[str]]:
|
||||
# Load carriers
|
||||
items: Dict[ShipUnitType, Set[str]] = defaultdict(Set[str])
|
||||
carriers = json.get("carriers", {})
|
||||
for carrier_shiptype, shipnames in carriers.items():
|
||||
shiptype = ShipUnitType.named(carrier_shiptype)
|
||||
if shiptype is not None:
|
||||
items[shiptype] = shipnames
|
||||
return items
|
||||
|
||||
@@ -201,8 +201,6 @@ class Migrator:
|
||||
c.faction.air_defense_units = set(c.faction.air_defense_units)
|
||||
if isinstance(c.faction.missiles, list):
|
||||
c.faction.missiles = set(c.faction.missiles)
|
||||
if isinstance(c.faction.carrier_names, list):
|
||||
c.faction.carrier_names = set(c.faction.carrier_names)
|
||||
if isinstance(c.faction.helicopter_carrier_names, list):
|
||||
c.faction.helicopter_carrier_names = set(
|
||||
c.faction.helicopter_carrier_names
|
||||
|
||||
@@ -12,7 +12,7 @@ from game import Game
|
||||
from game.factions.faction import Faction
|
||||
from game.naming import namegen
|
||||
from game.scenery_group import SceneryGroup
|
||||
from game.theater import PointWithHeading, PresetLocation
|
||||
from game.theater import PointWithHeading, PresetLocation, NavalControlPoint
|
||||
from game.theater.theatergroundobject import (
|
||||
BuildingGroundObject,
|
||||
IadsBuildingGroundObject,
|
||||
@@ -30,8 +30,10 @@ from .theatergroup import IadsGroundGroup, IadsRole, SceneryUnit, TheaterGroup
|
||||
from ..armedforces.armedforces import ArmedForces
|
||||
from ..armedforces.forcegroup import ForceGroup
|
||||
from ..campaignloader.campaignairwingconfig import CampaignAirWingConfig
|
||||
from ..campaignloader.campaigncarrierconfig import CampaignCarrierConfig
|
||||
from ..campaignloader.campaigngroundconfig import TgoConfig
|
||||
from ..data.groups import GroupTask
|
||||
from ..dcs.shipunittype import ShipUnitType
|
||||
from ..profiling import logged_duration
|
||||
from ..settings import Settings
|
||||
|
||||
@@ -49,6 +51,7 @@ class GeneratorSettings:
|
||||
no_player_navy: bool
|
||||
no_enemy_navy: bool
|
||||
tgo_config: TgoConfig
|
||||
carrier_config: CampaignCarrierConfig
|
||||
squadrons_start_full: bool
|
||||
|
||||
|
||||
@@ -125,7 +128,7 @@ class GameGenerator:
|
||||
|
||||
def should_remove_carrier(self, player: bool) -> bool:
|
||||
faction = self.player if player else self.enemy
|
||||
return self.generator_settings.no_carrier or not faction.carrier_names
|
||||
return self.generator_settings.no_carrier or not faction.carriers
|
||||
|
||||
def should_remove_lha(self, player: bool) -> bool:
|
||||
faction = self.player if player else self.enemy
|
||||
@@ -221,14 +224,49 @@ class GenericCarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
||||
carrier = next(self.control_point.ground_objects[-1].units)
|
||||
carrier.name = carrier_name
|
||||
|
||||
def apply_carrier_config(self) -> None:
|
||||
assert isinstance(self.control_point, NavalControlPoint)
|
||||
# If the campaign designer has specified a preferred name, use that
|
||||
# Note that the preferred name needs to exist in the faction, so we
|
||||
# don't end up with Kuznetsov carriers called CV-59 Forrestal
|
||||
preferred_name = None
|
||||
preferred_type = None
|
||||
carrier_map = self.generator_settings.carrier_config.by_original_name
|
||||
if ccfg := carrier_map.get(self.control_point.name):
|
||||
preferred_name = ccfg.preferred_name
|
||||
preferred_type = ccfg.preferred_type
|
||||
carrier_unit = self.control_point.ground_objects[0].groups[0].units[0]
|
||||
carrier_type = preferred_type if preferred_type else carrier_unit.unit_type
|
||||
assert isinstance(carrier_type, ShipUnitType)
|
||||
if preferred_type and self.faction.has_access_to_dcs_type(preferred_type.dcs_unit_type):
|
||||
carrier_unit.type = carrier_type.dcs_unit_type
|
||||
if (
|
||||
preferred_name
|
||||
):
|
||||
self.control_point.name = preferred_name
|
||||
else:
|
||||
# Otherwise pick randomly from the names specified for that particular carrier type
|
||||
carrier_names = self.faction.carriers.get(carrier_type)
|
||||
if carrier_names:
|
||||
self.control_point.name = random.choice(list(carrier_names))
|
||||
else:
|
||||
self.control_point.name = carrier_type.display_name
|
||||
# Prevents duplicate carrier or LHA names in campaigns with more that one of either.
|
||||
for carrier_type_key in self.faction.carriers:
|
||||
for carrier_name in self.faction.carriers[carrier_type_key]:
|
||||
if carrier_name == self.control_point.name:
|
||||
self.faction.carriers[carrier_type_key].remove(
|
||||
self.control_point.name
|
||||
)
|
||||
|
||||
|
||||
class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
||||
def generate(self) -> bool:
|
||||
if not super().generate():
|
||||
return False
|
||||
|
||||
carrier_names = self.faction.carrier_names
|
||||
if not carrier_names:
|
||||
carriers = self.faction.carriers
|
||||
if not carriers:
|
||||
logging.info(
|
||||
f"Skipping generation of {self.control_point.name} because "
|
||||
f"{self.faction_name} has no carriers"
|
||||
@@ -239,6 +277,7 @@ class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
||||
if not unit_group:
|
||||
logging.error(f"{self.faction_name} has no access to AircraftCarrier")
|
||||
return False
|
||||
|
||||
self.generate_ground_object_from_group(
|
||||
unit_group,
|
||||
PresetLocation(
|
||||
@@ -248,7 +287,7 @@ class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
||||
),
|
||||
GroupTask.AIRCRAFT_CARRIER,
|
||||
)
|
||||
self.update_carrier_name(random.choice(list(carrier_names)))
|
||||
self.apply_carrier_config()
|
||||
return True
|
||||
|
||||
|
||||
@@ -280,7 +319,7 @@ class LhaGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
||||
),
|
||||
GroupTask.HELICOPTER_CARRIER,
|
||||
)
|
||||
self.update_carrier_name(random.choice(list(lha_names)))
|
||||
self.apply_carrier_config()
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -317,6 +317,7 @@ def create_game(
|
||||
no_player_navy=False,
|
||||
no_enemy_navy=False,
|
||||
tgo_config=campaign.load_ground_forces_config(),
|
||||
carrier_config=campaign.load_carrier_config(),
|
||||
),
|
||||
ModSettings(
|
||||
a4_skyhawk=False,
|
||||
|
||||
@@ -84,6 +84,7 @@ class NewGameWizard(QtWidgets.QWizard):
|
||||
no_player_navy=self.field("no_player_navy"),
|
||||
no_enemy_navy=self.field("no_enemy_navy"),
|
||||
tgo_config=campaign.load_ground_forces_config(),
|
||||
carrier_config=campaign.load_carrier_config(),
|
||||
squadrons_start_full=self.field("squadrons_start_full"),
|
||||
)
|
||||
mod_settings = ModSettings(
|
||||
|
||||
@@ -81,6 +81,14 @@ settings:
|
||||
#default="Full"
|
||||
|
||||
|
||||
carriers:
|
||||
Naval-1:
|
||||
preferred_name: CVN-74
|
||||
preferred_type: CVN-74 John C. Stennis
|
||||
Naval-2:
|
||||
preferred_name: The Harry
|
||||
preferred_type: CVN-75 Harry S. Truman
|
||||
|
||||
|
||||
#Squadrons and order of battle settings
|
||||
squadrons:
|
||||
@@ -156,7 +164,7 @@ squadrons:
|
||||
aircraft:
|
||||
- CH-53E
|
||||
size: 2
|
||||
#CV-59 Forrestal
|
||||
#CV-59 Forrestal
|
||||
Naval-2:
|
||||
- primary: AEW&C
|
||||
aircraft:
|
||||
|
||||
@@ -46,14 +46,14 @@
|
||||
],
|
||||
"preset_groups": [
|
||||
"Hawk",
|
||||
"Patriot"
|
||||
"Patriot",
|
||||
"Carrier Strike Group 8"
|
||||
],
|
||||
"naval_units": [
|
||||
"FFG Oliver Hazard Perry",
|
||||
"DDG Arleigh Burke IIa",
|
||||
"CG Ticonderoga",
|
||||
"LHA-1 Tarawa",
|
||||
"CVN-74 John C. Stennis"
|
||||
"LHA-1 Tarawa"
|
||||
],
|
||||
"missiles": [],
|
||||
"air_defense_units": [
|
||||
|
||||
@@ -2,7 +2,11 @@ name: Carrier Strike Group 8
|
||||
tasks:
|
||||
- Navy
|
||||
units:
|
||||
- CVN-71 Theodore Roosevelt
|
||||
- CVN-72 Abraham Lincoln
|
||||
- CVN-73 George Washington
|
||||
- CVN-74 John C. Stennis
|
||||
- CVN-75 Harry S. Truman
|
||||
- DDG Arleigh Burke IIa
|
||||
- CG Ticonderoga
|
||||
layouts:
|
||||
|
||||
4
resources/units/ships/CVN_71.yaml
Normal file
4
resources/units/ships/CVN_71.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
class: AircraftCarrier
|
||||
price: 0
|
||||
variants:
|
||||
CVN-71 Theodore Roosevelt: null
|
||||
4
resources/units/ships/CVN_72.yaml
Normal file
4
resources/units/ships/CVN_72.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
class: AircraftCarrier
|
||||
price: 0
|
||||
variants:
|
||||
CVN-72 Abraham Lincoln: null
|
||||
4
resources/units/ships/CVN_73.yaml
Normal file
4
resources/units/ships/CVN_73.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
class: AircraftCarrier
|
||||
price: 0
|
||||
variants:
|
||||
CVN-73 George Washington: null
|
||||
4
resources/units/ships/CVN_75.yaml
Normal file
4
resources/units/ships/CVN_75.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
class: AircraftCarrier
|
||||
price: 0
|
||||
variants:
|
||||
CVN-75 Harry S. Truman: null
|
||||
4
resources/units/ships/CV_1143_5.yaml
Normal file
4
resources/units/ships/CV_1143_5.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
class: AircraftCarrier
|
||||
price: 0
|
||||
variants:
|
||||
CV 1143.5 Admiral Kuznetsov(2017): null
|
||||
@@ -92,12 +92,13 @@ class TestFactionLoader(unittest.TestCase):
|
||||
self.assertIn(Infantry.Soldier_M249, faction.infantry_units)
|
||||
|
||||
self.assertIn(Stennis.name, faction.naval_units)
|
||||
self.assertIn(Stennis, faction.carriers.keys())
|
||||
self.assertIn(LHA_Tarawa.name, faction.naval_units)
|
||||
|
||||
self.assertIn("mod", faction.requirements.keys())
|
||||
self.assertIn("Some mod is required", faction.requirements.values())
|
||||
|
||||
self.assertEqual(4, len(faction.carrier_names))
|
||||
self.assertEqual(4, len(faction.carriers))
|
||||
self.assertEqual(5, len(faction.helicopter_carrier_names))
|
||||
|
||||
@pytest.mark.skip(reason="Faction unit names in the json files are outdated")
|
||||
|
||||
Reference in New Issue
Block a user