diff --git a/game/ato/loadouts.py b/game/ato/loadouts.py index 158afbf22..5fb99daa4 100644 --- a/game/ato/loadouts.py +++ b/game/ato/loadouts.py @@ -10,6 +10,8 @@ from game.data.weapons import Pylon, Weapon, WeaponType from game.dcs.aircrafttype import AircraftType +from game.factions.faction import Faction + from .flighttype import FlightType if TYPE_CHECKING: @@ -52,6 +54,7 @@ def _fallback_for( weapon: Weapon, pylon: Pylon, date: datetime.date, + faction: Faction, skip_types: Optional[Iterable[WeaponType]] = None, ) -> Optional[Weapon]: if skip_types is None: @@ -59,14 +62,16 @@ def _fallback_for( for fallback in weapon.fallbacks: if not pylon.can_equip(fallback): continue - if not fallback.available_on(date): + if not fallback.available_on(date, faction): continue if fallback.weapon_group.type in skip_types: continue return fallback return None - def degrade_for_date(self, unit_type: AircraftType, date: datetime.date) -> Loadout: + def degrade_for_date( + self, unit_type: AircraftType, date: datetime.date, faction: Faction + ) -> Loadout: if self.date is not None and self.date <= date: return Loadout(self.name, self.pylons, self.date, self.is_custom) @@ -75,9 +80,9 @@ def degrade_for_date(self, unit_type: AircraftType, date: datetime.date) -> Load if weapon is None: del new_pylons[pylon_number] continue - if not weapon.available_on(date): + if not weapon.available_on(date, faction): pylon = Pylon.for_aircraft(unit_type, pylon_number) - fallback = self._fallback_for(weapon, pylon, date) + fallback = self._fallback_for(weapon, pylon, date, faction) if fallback is None: del new_pylons[pylon_number] else: @@ -89,11 +94,11 @@ def degrade_for_date(self, unit_type: AircraftType, date: datetime.date) -> Load # If the loadout was chosen explicitly by the user, assume they know what # they're doing. They may be coordinating buddy-lase. if not loadout.is_custom: - loadout.replace_lgbs_if_no_tgp(unit_type, date) + loadout.replace_lgbs_if_no_tgp(unit_type, date, faction) return loadout def replace_lgbs_if_no_tgp( - self, unit_type: AircraftType, date: datetime.date + self, unit_type: AircraftType, date: datetime.date, faction: Faction ) -> None: if self.has_weapon_of_type(WeaponType.TGP): return @@ -106,7 +111,7 @@ def replace_lgbs_if_no_tgp( if weapon is not None and weapon.weapon_group.type is WeaponType.LGB: pylon = Pylon.for_aircraft(unit_type, pylon_number) fallback = self._fallback_for( - weapon, pylon, date, skip_types={WeaponType.LGB} + weapon, pylon, date, faction, skip_types={WeaponType.LGB} ) if fallback is None: del new_pylons[pylon_number] diff --git a/game/data/weapons.py b/game/data/weapons.py index 13df6d876..dac7eb314 100644 --- a/game/data/weapons.py +++ b/game/data/weapons.py @@ -14,6 +14,8 @@ from dcs.weapons_data import weapon_ids from game.dcs.aircrafttype import AircraftType +from game.factions.faction import Faction + PydcsWeapon = Any PydcsWeaponAssignment = tuple[int, PydcsWeapon] @@ -77,8 +79,12 @@ def _load_all(cls) -> None: WeaponGroup.load_all() cls._loaded = True - def available_on(self, date: datetime.date) -> bool: + def available_on(self, date: datetime.date, faction: Faction) -> bool: introduction_year = self.weapon_group.introduction_year + if self.weapon_group.name in faction.weapons_introduction_year_overrides: + introduction_year = faction.weapons_introduction_year_overrides[ + self.weapon_group.name + ] if introduction_year is None: return True return date >= datetime.date(introduction_year, 1, 1) @@ -243,9 +249,9 @@ def equip(self, unit: FlyingUnit, weapon: Weapon) -> None: def make_pydcs_assignment(self, weapon: Weapon) -> PydcsWeaponAssignment: return self.number, weapon.pydcs_data - def available_on(self, date: datetime.date) -> Iterator[Weapon]: + def available_on(self, date: datetime.date, faction: Faction) -> Iterator[Weapon]: for weapon in self.allowed: - if weapon.available_on(date): + if weapon.available_on(date, faction): yield weapon @classmethod diff --git a/game/factions/faction.py b/game/factions/faction.py index 6cb5f8ccf..49a44dc35 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -3,6 +3,7 @@ import itertools import logging from dataclasses import dataclass, field +import datetime from functools import cached_property from typing import Any, Dict, Iterator, List, Optional, TYPE_CHECKING, Type @@ -118,6 +119,10 @@ class Faction: #: both will use it. unrestricted_satnav: bool = False + #: Overrides default weapons introduction years for faction. Maps names of + #: weapons groups to their introduction years. + weapons_introduction_year_overrides: Dict[str, int] = field(default_factory=dict) + def has_access_to_dcs_type(self, unit_type: Type[DcsUnitType]) -> bool: # Vehicle and Ship Units if any(unit_type == u.dcs_unit_type for u in self.accessible_units): @@ -262,6 +267,10 @@ def from_dict(cls: Type[Faction], json: Dict[str, Any]) -> Faction: faction.unrestricted_satnav = json.get("unrestricted_satnav", False) + faction.weapons_introduction_year_overrides = json.get( + "weapons_introduction_year_overrides", {} + ) + return faction @property diff --git a/game/missiongenerator/aircraft/flightgroupconfigurator.py b/game/missiongenerator/aircraft/flightgroupconfigurator.py index 951d37560..3eeb0b95a 100644 --- a/game/missiongenerator/aircraft/flightgroupconfigurator.py +++ b/game/missiongenerator/aircraft/flightgroupconfigurator.py @@ -245,7 +245,11 @@ def setup_payload(self, unit: FlyingUnit, member: FlightMember) -> None: loadout = member.loadout if self.game.settings.restrict_weapons_by_date: - loadout = loadout.degrade_for_date(self.flight.unit_type, self.game.date) + loadout = loadout.degrade_for_date( + self.flight.unit_type, + self.game.date, + self.flight.squadron.coalition.faction, + ) for pylon_number, weapon in loadout.pylons.items(): if weapon is None: diff --git a/qt_ui/windows/mission/flight/payload/QPylonEditor.py b/qt_ui/windows/mission/flight/payload/QPylonEditor.py index 5e3d013ce..059aaafba 100644 --- a/qt_ui/windows/mission/flight/payload/QPylonEditor.py +++ b/qt_ui/windows/mission/flight/payload/QPylonEditor.py @@ -26,7 +26,9 @@ def __init__( self.addItem("None", None) if self.game.settings.restrict_weapons_by_date: - weapons = pylon.available_on(self.game.date) + weapons = pylon.available_on( + self.game.date, flight.squadron.coalition.faction + ) else: weapons = pylon.allowed allowed = sorted(weapons, key=operator.attrgetter("name")) @@ -68,7 +70,11 @@ def weapon_from_loadout(self, loadout: Loadout) -> Optional[Weapon]: def matching_weapon_name(self, loadout: Loadout) -> str: if self.game.settings.restrict_weapons_by_date: - loadout = loadout.degrade_for_date(self.flight.unit_type, self.game.date) + loadout = loadout.degrade_for_date( + self.flight.unit_type, + self.game.date, + self.flight.squadron.coalition.faction, + ) weapon = self.weapon_from_loadout(loadout) if weapon is None: return "None" diff --git a/resources/factions/egypt_1973.yaml b/resources/factions/egypt_1973.yaml index e9a5f7986..8ec06bfae 100644 --- a/resources/factions/egypt_1973.yaml +++ b/resources/factions/egypt_1973.yaml @@ -42,3 +42,7 @@ air_defense_units: - ZSU-57-2 'Sparka' has_jtac: false doctrine: coldwar +weapons_introduction_year_overrides: + R-3R - AAM, radar guided: 1980 + R-60 x 2: 1980 + R-60: 1980 \ No newline at end of file diff --git a/resources/factions/nva_1970.yaml b/resources/factions/nva_1970.yaml index 7963d50b6..5f313d41e 100644 --- a/resources/factions/nva_1970.yaml +++ b/resources/factions/nva_1970.yaml @@ -42,4 +42,8 @@ air_defense_units: - ZU-23 on Ural-375 - ZSU-23-4 Shilka has_jtac: "false" -doctrine: "coldwar" \ No newline at end of file +doctrine: "coldwar" +weapons_introduction_year_overrides: + R-3R - AAM, radar guided: 1980 + R-60 x 2: 1980 + R-60: 1980 \ No newline at end of file diff --git a/resources/factions/syria_1967.yaml b/resources/factions/syria_1967.yaml index 3e7fac3c0..deb91570c 100644 --- a/resources/factions/syria_1967.yaml +++ b/resources/factions/syria_1967.yaml @@ -46,3 +46,7 @@ helicopter_carrier_names: [] requirements: {} carrier_names: [] doctrine: coldwar +weapons_introduction_year_overrides: + R-3R - AAM, radar guided: 1980 + R-60 x 2: 1980 + R-60: 1980 \ No newline at end of file diff --git a/resources/factions/syria_1967_with_ww2_weapons.yaml b/resources/factions/syria_1967_with_ww2_weapons.yaml index 9f8cb70a4..08ffa873a 100644 --- a/resources/factions/syria_1967_with_ww2_weapons.yaml +++ b/resources/factions/syria_1967_with_ww2_weapons.yaml @@ -52,3 +52,7 @@ carrier_names: [] requirements: WW2 Asset Pack: https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/ doctrine: coldwar +weapons_introduction_year_overrides: + R-3R - AAM, radar guided: 1980 + R-60 x 2: 1980 + R-60: 1980 \ No newline at end of file diff --git a/resources/factions/syria_1973.yaml b/resources/factions/syria_1973.yaml index f7f44a787..8a693d7ae 100644 --- a/resources/factions/syria_1973.yaml +++ b/resources/factions/syria_1973.yaml @@ -51,3 +51,7 @@ helicopter_carrier_names: [] requirements: {} carrier_names: [] doctrine: coldwar +weapons_introduction_year_overrides: + R-3R - AAM, radar guided: 1980 + R-60 x 2: 1980 + R-60: 1980 \ No newline at end of file