mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Merge branch 'develop' into dependabot/npm_and_yarn/client/multi-7d5e57b41f
This commit is contained in:
commit
f611620276
@ -7,6 +7,7 @@ Saves from 11.x are not compatible with 12.0.0.
|
||||
* **[Engine]** Support for DCS 2.9.9.2280.
|
||||
* **[Campaign]** Flights are assigned different callsigns appropriate to the faction.
|
||||
* **[Campaign]** Removed deprecated settings for generating persistent and invulnerable AWACs and tankers.
|
||||
* **[Data]** Added ability to restrict weapons usage for a faction to a different year from the nominal weapon introduction year. Updated faction data to restrict more advanced missiles from Soviet client states during the cold war. Updated Egypt 2000 faction to restrict AIM-120 usage.
|
||||
* **[Mission Generation]** Added option to skip combat when fast forwarding, which progresses fast forward as if the combat did not occur. Simplified fast forward settings by consolidating "Fast forward mission to first contact" and "Player missions interrupt fast forward" into a single setting and expanding options for "Auto-resolve combat during fast-forward (WIP)".
|
||||
* **[Mods]** F/A-18 E/F/G Super Hornet mod version updated to 2.3.
|
||||
|
||||
|
||||
@ -10,6 +10,8 @@ from dcs.unittype import FlyingType
|
||||
|
||||
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 @@ class Loadout:
|
||||
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 @@ class Loadout:
|
||||
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 @@ class Loadout:
|
||||
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 @@ class Loadout:
|
||||
# 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 @@ class Loadout:
|
||||
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]
|
||||
|
||||
@ -104,4 +104,6 @@ class PackageBuilder:
|
||||
"""Returns any planned flights to the inventory."""
|
||||
flights = list(self.package.flights)
|
||||
for flight in flights:
|
||||
if flight.callsign is not None:
|
||||
self.callsign_generator.release_callsign(flight.callsign)
|
||||
self.package.remove_flight(flight)
|
||||
|
||||
@ -2,11 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
from typing import Any, ClassVar
|
||||
from typing import Any, ClassVar, Optional
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
|
||||
from dcs.task import OptAAMissileAttackRange
|
||||
from game.data.units import UnitClass
|
||||
from game.utils import Distance, feet, nautical_miles
|
||||
|
||||
@ -115,6 +115,28 @@ class Cap:
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Tactics:
|
||||
#: Aircraft use countermeasures (chaff, flares)
|
||||
use_countermeasures: bool
|
||||
|
||||
#: Air-to-air missile attack range options
|
||||
air_to_air_missile_attack_range: Optional[OptAAMissileAttackRange.Values]
|
||||
|
||||
#: Air defence units evade ARMs
|
||||
air_defence_evades_anti_radiation_missiles: bool
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: dict[str, Any]) -> Tactics:
|
||||
return Tactics(
|
||||
use_countermeasures=data.get("use_countermeasures", True),
|
||||
air_to_air_missile_attack_range=None,
|
||||
air_defence_evades_anti_radiation_missiles=data.get(
|
||||
"air_defence_evades_anti_radiation_missiles", False
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Doctrine:
|
||||
#: Name of the doctrine, used to assign a doctrine in a faction.
|
||||
@ -159,6 +181,9 @@ class Doctrine:
|
||||
#: Doctrine for Fighter Sweep missions.
|
||||
sweep: Sweep
|
||||
|
||||
#: Tactics options
|
||||
tactics: Tactics
|
||||
|
||||
_by_name: ClassVar[dict[str, Doctrine]] = {}
|
||||
_loaded: ClassVar[bool] = False
|
||||
|
||||
@ -219,6 +244,7 @@ class Doctrine:
|
||||
cas=Cas.from_dict(data["cas"]),
|
||||
cap=Cap.from_dict(data["cap"]),
|
||||
sweep=Sweep.from_dict(data["sweep"]),
|
||||
tactics=Tactics.from_dict(data.get("tactics", {})),
|
||||
)
|
||||
)
|
||||
cls._loaded = True
|
||||
|
||||
@ -14,6 +14,8 @@ from dcs.flyingunit import FlyingUnit
|
||||
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 @@ class Weapon:
|
||||
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 @@ class Pylon:
|
||||
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
|
||||
|
||||
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
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 @@ class Faction:
|
||||
|
||||
faction.unrestricted_satnav = json.get("unrestricted_satnav", False)
|
||||
|
||||
faction.weapons_introduction_year_overrides = json.get(
|
||||
"weapons_introduction_year_overrides", {}
|
||||
)
|
||||
|
||||
return faction
|
||||
|
||||
@property
|
||||
|
||||
@ -245,7 +245,11 @@ class FlightGroupConfigurator:
|
||||
|
||||
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:
|
||||
|
||||
@ -26,7 +26,9 @@ class QPylonEditor(QComboBox):
|
||||
|
||||
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 @@ class QPylonEditor(QComboBox):
|
||||
|
||||
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"
|
||||
|
||||
@ -31,4 +31,7 @@ helicopter:
|
||||
combat_altitude_ft_agl: 200
|
||||
rendezvous_altitude_ft_agl: 1500
|
||||
air_assault_nav_altitude_ft_agl: 1500
|
||||
|
||||
tactics:
|
||||
use_countermeasures: true
|
||||
air_defence_evades_anti_radiation_missiles: false
|
||||
air_to_air_missile_attack_range: ~
|
||||
|
||||
@ -31,3 +31,7 @@ helicopter:
|
||||
combat_altitude_ft_agl: 200
|
||||
rendezvous_altitude_ft_agl: 1500
|
||||
air_assault_nav_altitude_ft_agl: 1500
|
||||
tactics:
|
||||
use_countermeasures: true
|
||||
air_defence_evades_anti_radiation_missiles: false
|
||||
air_to_air_missile_attack_range: ~
|
||||
@ -30,3 +30,7 @@ helicopter:
|
||||
combat_altitude_ft_agl: 200
|
||||
rendezvous_altitude_ft_agl: 1500
|
||||
air_assault_nav_altitude_ft_agl: 1500
|
||||
tactics:
|
||||
use_countermeasures: true
|
||||
air_defence_evades_anti_radiation_missiles: false
|
||||
air_to_air_missile_attack_range: ~
|
||||
@ -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
|
||||
@ -82,3 +82,8 @@ air_defense_units:
|
||||
- ZSU-57-2 'Sparka'
|
||||
has_jtac: true
|
||||
jtac_unit: MQ-9 Reaper
|
||||
weapons_introduction_year_overrides:
|
||||
AIM-120B: 2050
|
||||
2xAIM-120B: 2050
|
||||
AIM-120C: 2050
|
||||
2xAIM-120C: 2050
|
||||
@ -42,4 +42,8 @@ air_defense_units:
|
||||
- ZU-23 on Ural-375
|
||||
- ZSU-23-4 Shilka
|
||||
has_jtac: "false"
|
||||
doctrine: "coldwar"
|
||||
doctrine: "coldwar"
|
||||
weapons_introduction_year_overrides:
|
||||
R-3R - AAM, radar guided: 1980
|
||||
R-60 x 2: 1980
|
||||
R-60: 1980
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -6,3 +6,4 @@ clsids:
|
||||
- "{LAU-115 - AIM-7E}"
|
||||
- "{SHOULDER AIM-7E}"
|
||||
- "{BELLY AIM-7E}"
|
||||
- "{HB_F4E_AIM-7E}"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: AIM-7F
|
||||
year: 1976
|
||||
fallback: AIM-7E
|
||||
fallback: AIM-7E-2
|
||||
clsids:
|
||||
- "{SHOULDER AIM-7F}"
|
||||
- "{BELLY AIM-7F}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user