mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Configurable carriers
This commit is contained in:
parent
e416e07366
commit
d2fd7bbb4e
@ -8,6 +8,7 @@
|
|||||||
* **[Squadrons]** Ability to define a livery-set for each squadron from which Retribution will randomly choose during mission generation
|
* **[Squadrons]** Ability to define a livery-set for each squadron from which Retribution will randomly choose during mission generation
|
||||||
* **[Modding]** Updated support for F/A-18E/F/G mod version 2.2.5
|
* **[Modding]** Updated support for F/A-18E/F/G mod version 2.2.5
|
||||||
* **[Campaign Setup]** Allow adjustments to naval TGOs (except carriers) on turn 0
|
* **[Campaign Setup]** Allow adjustments to naval TGOs (except carriers) on turn 0
|
||||||
|
* **[Campaign Design]** Ability to configure specific carrier names & types in campaign's yaml file
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
* **[UI/UX]** A-10A flights can be edited again.
|
* **[UI/UX]** A-10A flights can be edited again.
|
||||||
|
|||||||
@ -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 .campaigncarrierconfig import CampaignCarrierConfig
|
||||||
from .campaigngroundconfig import TgoConfig
|
from .campaigngroundconfig import TgoConfig
|
||||||
from .mizcampaignloader import MizCampaignLoader
|
from .mizcampaignloader import MizCampaignLoader
|
||||||
from ..factions import FACTIONS, Faction
|
from ..factions import FACTIONS, Faction
|
||||||
@ -164,6 +165,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_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:
|
def load_ground_forces_config(self) -> TgoConfig:
|
||||||
ground_forces = self.data.get("ground_forces", {})
|
ground_forces = self.data.get("ground_forces", {})
|
||||||
if not 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 itertools
|
||||||
import logging
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Optional, Dict, Type, List, Any, Iterator, TYPE_CHECKING, Set
|
from typing import Optional, Dict, Type, List, Any, Iterator, TYPE_CHECKING, Set
|
||||||
@ -93,11 +94,8 @@ class Faction:
|
|||||||
# Required mods or asset packs
|
# Required mods or asset packs
|
||||||
requirements: Dict[str, str] = field(default_factory=dict)
|
requirements: Dict[str, str] = field(default_factory=dict)
|
||||||
|
|
||||||
# Possible carrier names
|
# Possible carrier units mapped to names
|
||||||
carrier_names: Set[str] = field(default_factory=set)
|
carriers: Dict[ShipUnitType, Set[str]] = field(default_factory=dict)
|
||||||
|
|
||||||
# Possible helicopter carrier names
|
|
||||||
helicopter_carrier_names: Set[str] = field(default_factory=set)
|
|
||||||
|
|
||||||
# Available Naval Units
|
# Available Naval Units
|
||||||
naval_units: Set[ShipUnitType] = field(default_factory=set)
|
naval_units: Set[ShipUnitType] = field(default_factory=set)
|
||||||
@ -241,8 +239,30 @@ class Faction:
|
|||||||
|
|
||||||
faction.requirements = json.get("requirements", {})
|
faction.requirements = json.get("requirements", {})
|
||||||
|
|
||||||
faction.carrier_names = json.get("carrier_names", [])
|
# First try to load the carriers in the new format which
|
||||||
faction.helicopter_carrier_names = json.get("helicopter_carrier_names", [])
|
# 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", [])
|
||||||
|
helicopter_carrier_names = json.get("helicopter_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] = helicopter_carrier_names
|
||||||
|
|
||||||
|
faction.carriers = loaded_carriers
|
||||||
|
faction.naval_units.union(faction.carriers.keys())
|
||||||
|
|
||||||
faction.has_jtac = json.get("has_jtac", False)
|
faction.has_jtac = json.get("has_jtac", False)
|
||||||
jtac_name = json.get("jtac_unit", None)
|
jtac_name = json.get("jtac_unit", None)
|
||||||
@ -596,3 +616,14 @@ def load_all_ships(data: list[str]) -> List[Type[ShipType]]:
|
|||||||
if item is not None:
|
if item is not None:
|
||||||
items.append(item)
|
items.append(item)
|
||||||
return items
|
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
|
||||||
|
|||||||
@ -208,12 +208,6 @@ class Migrator:
|
|||||||
c.faction.air_defense_units = set(c.faction.air_defense_units)
|
c.faction.air_defense_units = set(c.faction.air_defense_units)
|
||||||
if isinstance(c.faction.missiles, list):
|
if isinstance(c.faction.missiles, list):
|
||||||
c.faction.missiles = set(c.faction.missiles)
|
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
|
|
||||||
)
|
|
||||||
if isinstance(c.faction.naval_units, list):
|
if isinstance(c.faction.naval_units, list):
|
||||||
c.faction.naval_units = set(c.faction.naval_units)
|
c.faction.naval_units = set(c.faction.naval_units)
|
||||||
if isinstance(c.faction.building_set, list):
|
if isinstance(c.faction.building_set, list):
|
||||||
|
|||||||
@ -1395,6 +1395,11 @@ class NavalControlPoint(
|
|||||||
L02,
|
L02,
|
||||||
L52,
|
L52,
|
||||||
L61,
|
L61,
|
||||||
|
CV_1143_5,
|
||||||
|
CVN_71,
|
||||||
|
CVN_72,
|
||||||
|
CVN_73,
|
||||||
|
CVN_75,
|
||||||
]:
|
]:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -7,12 +7,13 @@ from datetime import datetime, time
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import dcs.statics
|
import dcs.statics
|
||||||
|
from dcs.countries import country_dict
|
||||||
|
|
||||||
from game import Game
|
from game import Game
|
||||||
from game.factions.faction import Faction
|
from game.factions.faction import Faction
|
||||||
from game.naming import namegen
|
from game.naming import namegen
|
||||||
from game.scenery_group import SceneryGroup
|
from game.scenery_group import SceneryGroup
|
||||||
from game.theater import PointWithHeading, PresetLocation
|
from game.theater import PointWithHeading, PresetLocation, NavalControlPoint
|
||||||
from game.theater.theatergroundobject import (
|
from game.theater.theatergroundobject import (
|
||||||
BuildingGroundObject,
|
BuildingGroundObject,
|
||||||
IadsBuildingGroundObject,
|
IadsBuildingGroundObject,
|
||||||
@ -30,8 +31,11 @@ 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.campaigncarrierconfig import CampaignCarrierConfig
|
||||||
from ..campaignloader.campaigngroundconfig import TgoConfig
|
from ..campaignloader.campaigngroundconfig import TgoConfig
|
||||||
from ..data.groups import GroupTask
|
from ..data.groups import GroupTask
|
||||||
|
from ..data.units import UnitClass
|
||||||
|
from ..dcs.shipunittype import ShipUnitType
|
||||||
from ..profiling import logged_duration
|
from ..profiling import logged_duration
|
||||||
from ..settings import Settings
|
from ..settings import Settings
|
||||||
|
|
||||||
@ -49,6 +53,7 @@ class GeneratorSettings:
|
|||||||
no_player_navy: bool
|
no_player_navy: bool
|
||||||
no_enemy_navy: bool
|
no_enemy_navy: bool
|
||||||
tgo_config: TgoConfig
|
tgo_config: TgoConfig
|
||||||
|
carrier_config: CampaignCarrierConfig
|
||||||
squadrons_start_full: bool
|
squadrons_start_full: bool
|
||||||
|
|
||||||
|
|
||||||
@ -127,11 +132,13 @@ class GameGenerator:
|
|||||||
|
|
||||||
def should_remove_carrier(self, player: bool) -> bool:
|
def should_remove_carrier(self, player: bool) -> bool:
|
||||||
faction = self.player if player else self.enemy
|
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:
|
def should_remove_lha(self, player: bool) -> bool:
|
||||||
faction = self.player if player else self.enemy
|
faction = self.player if player else self.enemy
|
||||||
return self.generator_settings.no_lha or not faction.helicopter_carrier_names
|
return self.generator_settings.no_lha or not [
|
||||||
|
x for x in faction.carriers if x.unit_class == UnitClass.HELICOPTER_CARRIER
|
||||||
|
]
|
||||||
|
|
||||||
def prepare_theater(self) -> None:
|
def prepare_theater(self) -> None:
|
||||||
to_remove: List[ControlPoint] = []
|
to_remove: List[ControlPoint] = []
|
||||||
@ -223,14 +230,52 @@ class GenericCarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
carrier = next(self.control_point.ground_objects[-1].units)
|
carrier = next(self.control_point.ground_objects[-1].units)
|
||||||
carrier.name = carrier_name
|
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]
|
||||||
|
if preferred_type and preferred_type.dcs_unit_type in [
|
||||||
|
v
|
||||||
|
for k, v in country_dict[self.faction.country.id].Ship.__dict__.items() # type: ignore
|
||||||
|
if "__" not in k
|
||||||
|
]:
|
||||||
|
carrier_unit.type = preferred_type.dcs_unit_type
|
||||||
|
if preferred_name:
|
||||||
|
self.control_point.name = preferred_name
|
||||||
|
else:
|
||||||
|
carrier_type = preferred_type if preferred_type else carrier_unit.unit_type
|
||||||
|
assert isinstance(carrier_type, ShipUnitType)
|
||||||
|
# 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
|
||||||
|
carrier_unit.name = self.control_point.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):
|
class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
||||||
def generate(self) -> bool:
|
def generate(self) -> bool:
|
||||||
if not super().generate():
|
if not super().generate():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
carrier_names = self.faction.carrier_names
|
carriers = self.faction.carriers
|
||||||
if not carrier_names:
|
if not carriers:
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Skipping generation of {self.control_point.name} because "
|
f"Skipping generation of {self.control_point.name} because "
|
||||||
f"{self.faction_name} has no carriers"
|
f"{self.faction_name} has no carriers"
|
||||||
@ -241,6 +286,7 @@ class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
|||||||
if not unit_group:
|
if not unit_group:
|
||||||
logging.error(f"{self.faction_name} has no access to AircraftCarrier")
|
logging.error(f"{self.faction_name} has no access to AircraftCarrier")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.generate_ground_object_from_group(
|
self.generate_ground_object_from_group(
|
||||||
unit_group,
|
unit_group,
|
||||||
PresetLocation(
|
PresetLocation(
|
||||||
@ -250,7 +296,7 @@ class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
|||||||
),
|
),
|
||||||
GroupTask.AIRCRAFT_CARRIER,
|
GroupTask.AIRCRAFT_CARRIER,
|
||||||
)
|
)
|
||||||
self.update_carrier_name(random.choice(list(carrier_names)))
|
self.apply_carrier_config()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -259,8 +305,8 @@ class LhaGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
|||||||
if not super().generate():
|
if not super().generate():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
lha_names = self.faction.helicopter_carrier_names
|
lhas = self.faction.carriers
|
||||||
if not lha_names:
|
if not lhas:
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Skipping generation of {self.control_point.name} because "
|
f"Skipping generation of {self.control_point.name} because "
|
||||||
f"{self.faction_name} has no LHAs"
|
f"{self.faction_name} has no LHAs"
|
||||||
@ -282,7 +328,7 @@ class LhaGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
|||||||
),
|
),
|
||||||
GroupTask.HELICOPTER_CARRIER,
|
GroupTask.HELICOPTER_CARRIER,
|
||||||
)
|
)
|
||||||
self.update_carrier_name(random.choice(list(lha_names)))
|
self.apply_carrier_config()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -318,6 +318,7 @@ def create_game(
|
|||||||
no_player_navy=False,
|
no_player_navy=False,
|
||||||
no_enemy_navy=False,
|
no_enemy_navy=False,
|
||||||
tgo_config=campaign.load_ground_forces_config(),
|
tgo_config=campaign.load_ground_forces_config(),
|
||||||
|
carrier_config=campaign.load_carrier_config(),
|
||||||
),
|
),
|
||||||
ModSettings(
|
ModSettings(
|
||||||
a4_skyhawk=False,
|
a4_skyhawk=False,
|
||||||
|
|||||||
@ -84,6 +84,7 @@ class NewGameWizard(QtWidgets.QWizard):
|
|||||||
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(),
|
tgo_config=campaign.load_ground_forces_config(),
|
||||||
|
carrier_config=campaign.load_carrier_config(),
|
||||||
squadrons_start_full=self.field("squadrons_start_full"),
|
squadrons_start_full=self.field("squadrons_start_full"),
|
||||||
)
|
)
|
||||||
mod_settings = ModSettings(
|
mod_settings = ModSettings(
|
||||||
|
|||||||
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,13 +92,13 @@ class TestFactionLoader(unittest.TestCase):
|
|||||||
self.assertIn(Infantry.Soldier_M249, faction.infantry_units)
|
self.assertIn(Infantry.Soldier_M249, faction.infantry_units)
|
||||||
|
|
||||||
self.assertIn(Stennis.name, faction.naval_units)
|
self.assertIn(Stennis.name, faction.naval_units)
|
||||||
|
self.assertIn(Stennis, faction.carriers.keys())
|
||||||
self.assertIn(LHA_Tarawa.name, faction.naval_units)
|
self.assertIn(LHA_Tarawa.name, faction.naval_units)
|
||||||
|
|
||||||
self.assertIn("mod", faction.requirements.keys())
|
self.assertIn("mod", faction.requirements.keys())
|
||||||
self.assertIn("Some mod is required", faction.requirements.values())
|
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")
|
@pytest.mark.skip(reason="Faction unit names in the json files are outdated")
|
||||||
def test_load_valid_faction_with_invalid_country(self) -> None:
|
def test_load_valid_faction_with_invalid_country(self) -> None:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user