mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Merge remote-tracking branch 'khopa/develop' into develop
This commit is contained in:
commit
a30d9276b8
12
changelog.md
12
changelog.md
@ -4,23 +4,21 @@ Saves from 2.4 are not compatible with 2.5.
|
|||||||
|
|
||||||
## Features/Improvements
|
## Features/Improvements
|
||||||
|
|
||||||
* **[Flight Planner]** (WIP) Added AEW&C missions. (by siKruger)
|
* **[Flight Planner]** Added AEW&C missions. (by siKruger)
|
||||||
* **[Kneeboard]** Added dark kneeboard option (by GvonH)
|
* **[Kneeboard]** Added dark kneeboard option (by GvonH)
|
||||||
|
* **[Campaigns]** Multiple EWR sites may now be generated, and EWR sites may be generated outside bases (by SnappyComebacks)
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
|
||||||
* **[Flight Planner]** Front lines now project threat zones, so TARCAP/escorts will not be pruned for flights near the front. Packages may also route around the front line when practical.
|
* **[Flight Planner]** Front lines now project threat zones, so TARCAP/escorts will not be pruned for flights near the front. Packages may also route around the front line when practical.
|
||||||
* **[Flight Planner]** Fixed error when planning BAI at SAMs with dead subgroups.
|
* **[Flight Planner]** Fixed error when planning BAI at SAMs with dead subgroups.
|
||||||
* **[Flight Planner]** Mig-19 was not allowed for CAS roles fixed
|
* **[Flight Planner]** Mig-19 was not allowed for CAS roles fixed
|
||||||
|
* **[Flight Planner]** Increased size of navigation planning area to avoid plannign failures with distant waypoints.
|
||||||
* **[Objective names]** Fixed typos in objective name : ARMADILLLO -> ARMADILLO (by SnappyComebacks)
|
* **[Objective names]** Fixed typos in objective name : ARMADILLLO -> ARMADILLO (by SnappyComebacks)
|
||||||
* **[Payloads]** F-86 Sabre was missing a custom payload
|
* **[Payloads]** F-86 Sabre was missing a custom payload
|
||||||
* **[Payloads]** Added GAR-8 period restrictions (by Mustang-25)
|
* **[Payloads]** Added GAR-8 period restrictions (by Mustang-25)
|
||||||
* **[Campaign]** Date now progresses.
|
* **[Campaign]** Date now progresses.
|
||||||
|
* **[Campaign]** Added game over message when a coalition runs out of functioning airbases.
|
||||||
# 2.4.4
|
|
||||||
|
|
||||||
## Fixes
|
|
||||||
|
|
||||||
* **[Mission Generation]** Fixed "invalid face handle" error in kneeboard generation that occurred on some machines.
|
* **[Mission Generation]** Fixed "invalid face handle" error in kneeboard generation that occurred on some machines.
|
||||||
|
|
||||||
# 2.4.3
|
# 2.4.3
|
||||||
|
|||||||
@ -118,6 +118,8 @@ class MizCampaignLoader:
|
|||||||
AirDefence.SAM_SA_3_S_125_Goa_LN.id,
|
AirDefence.SAM_SA_3_S_125_Goa_LN.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REQUIRED_EWR_UNIT_TYPE = AirDefence.EWR_1L13.id
|
||||||
|
|
||||||
BASE_DEFENSE_RADIUS = nautical_miles(2)
|
BASE_DEFENSE_RADIUS = nautical_miles(2)
|
||||||
|
|
||||||
def __init__(self, miz: Path, theater: ConflictTheater) -> None:
|
def __init__(self, miz: Path, theater: ConflictTheater) -> None:
|
||||||
@ -247,6 +249,12 @@ class MizCampaignLoader:
|
|||||||
if group.units[0].type in self.REQUIRED_MEDIUM_RANGE_SAM_UNIT_TYPES:
|
if group.units[0].type in self.REQUIRED_MEDIUM_RANGE_SAM_UNIT_TYPES:
|
||||||
yield group
|
yield group
|
||||||
|
|
||||||
|
@property
|
||||||
|
def required_ewrs(self) -> Iterator[VehicleGroup]:
|
||||||
|
for group in self.red.vehicle_group:
|
||||||
|
if group.units[0].type in self.REQUIRED_EWR_UNIT_TYPE:
|
||||||
|
yield group
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def helipads(self) -> Iterator[StaticGroup]:
|
def helipads(self) -> Iterator[StaticGroup]:
|
||||||
for group in self.blue.static_group:
|
for group in self.blue.static_group:
|
||||||
@ -356,9 +364,14 @@ class MizCampaignLoader:
|
|||||||
|
|
||||||
for group in self.ewrs:
|
for group in self.ewrs:
|
||||||
closest, distance = self.objective_info(group)
|
closest, distance = self.objective_info(group)
|
||||||
closest.preset_locations.ewrs.append(
|
if distance < self.BASE_DEFENSE_RADIUS:
|
||||||
PointWithHeading.from_point(group.position, group.units[0].heading)
|
closest.preset_locations.ewrs.append(
|
||||||
)
|
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
closest.preset_locations.ewrs.append(
|
||||||
|
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||||
|
)
|
||||||
|
|
||||||
for group in self.offshore_strike_targets:
|
for group in self.offshore_strike_targets:
|
||||||
closest, distance = self.objective_info(group)
|
closest, distance = self.objective_info(group)
|
||||||
@ -396,6 +409,12 @@ class MizCampaignLoader:
|
|||||||
PointWithHeading.from_point(group.position, group.units[0].heading)
|
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for group in self.required_ewrs:
|
||||||
|
closest, distance = self.objective_info(group)
|
||||||
|
closest.preset_locations.required_ewrs.append(
|
||||||
|
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||||
|
)
|
||||||
|
|
||||||
for group in self.helipads:
|
for group in self.helipads:
|
||||||
closest, distance = self.objective_info(group)
|
closest, distance = self.objective_info(group)
|
||||||
closest.helipads.append(
|
closest.helipads.append(
|
||||||
|
|||||||
@ -107,6 +107,9 @@ class PresetLocations:
|
|||||||
#: Locations of medium range SAMs which should always be spawned.
|
#: Locations of medium range SAMs which should always be spawned.
|
||||||
required_medium_range_sams: List[PointWithHeading] = field(default_factory=list)
|
required_medium_range_sams: List[PointWithHeading] = field(default_factory=list)
|
||||||
|
|
||||||
|
#: Locations of EWRs which should always be spawned.
|
||||||
|
required_ewrs: List[PointWithHeading] = field(default_factory=list)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _random_from(points: List[PointWithHeading]) -> Optional[PointWithHeading]:
|
def _random_from(points: List[PointWithHeading]) -> Optional[PointWithHeading]:
|
||||||
"""Finds, removes, and returns a random position from the given list."""
|
"""Finds, removes, and returns a random position from the given list."""
|
||||||
|
|||||||
@ -37,10 +37,8 @@ from gen.fleet.ship_group_generator import (
|
|||||||
from gen.locations.preset_location_finder import MizDataLocationFinder
|
from gen.locations.preset_location_finder import MizDataLocationFinder
|
||||||
from gen.missiles.missiles_group_generator import generate_missile_group
|
from gen.missiles.missiles_group_generator import generate_missile_group
|
||||||
from gen.sam.airdefensegroupgenerator import AirDefenseRange
|
from gen.sam.airdefensegroupgenerator import AirDefenseRange
|
||||||
from gen.sam.sam_group_generator import (
|
from gen.sam.sam_group_generator import generate_anti_air_group
|
||||||
generate_anti_air_group,
|
from gen.sam.ewr_group_generator import generate_ewr_group
|
||||||
generate_ewr_group,
|
|
||||||
)
|
|
||||||
from . import (
|
from . import (
|
||||||
ConflictTheater,
|
ConflictTheater,
|
||||||
ControlPoint,
|
ControlPoint,
|
||||||
@ -464,7 +462,11 @@ class BaseDefenseGenerator:
|
|||||||
group_id = self.game.next_group_id()
|
group_id = self.game.next_group_id()
|
||||||
|
|
||||||
g = EwrGroundObject(
|
g = EwrGroundObject(
|
||||||
namegen.random_objective_name(), group_id, position, self.control_point
|
namegen.random_objective_name(),
|
||||||
|
group_id,
|
||||||
|
position,
|
||||||
|
self.control_point,
|
||||||
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
group = generate_ewr_group(self.game, g, self.faction)
|
group = generate_ewr_group(self.game, g, self.faction)
|
||||||
@ -609,6 +611,7 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
def generate_ground_points(self) -> None:
|
def generate_ground_points(self) -> None:
|
||||||
"""Generate ground objects and AA sites for the control point."""
|
"""Generate ground objects and AA sites for the control point."""
|
||||||
skip_sams = self.generate_required_aa()
|
skip_sams = self.generate_required_aa()
|
||||||
|
skip_ewrs = self.generate_required_ewr()
|
||||||
|
|
||||||
if self.control_point.is_global:
|
if self.control_point.is_global:
|
||||||
return
|
return
|
||||||
@ -625,6 +628,12 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
skip_sams -= 1
|
skip_sams -= 1
|
||||||
else:
|
else:
|
||||||
self.generate_aa_site()
|
self.generate_aa_site()
|
||||||
|
# 1 in 4 additional objectives are EWR.
|
||||||
|
elif random.randint(0, 3) == 0:
|
||||||
|
if skip_ewrs > 0:
|
||||||
|
skip_ewrs -= 1
|
||||||
|
else:
|
||||||
|
self.generate_ewr_site()
|
||||||
else:
|
else:
|
||||||
self.generate_ground_point()
|
self.generate_ground_point()
|
||||||
|
|
||||||
@ -656,6 +665,17 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
presets.required_medium_range_sams
|
presets.required_medium_range_sams
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def generate_required_ewr(self) -> int:
|
||||||
|
"""Generates the EWR sites that are required by the campaign.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of EWR sites that were generated.
|
||||||
|
"""
|
||||||
|
presets = self.control_point.preset_locations
|
||||||
|
for position in presets.required_ewrs:
|
||||||
|
self.generate_ewr_at(position)
|
||||||
|
return len(presets.required_ewrs)
|
||||||
|
|
||||||
def generate_ground_point(self) -> None:
|
def generate_ground_point(self) -> None:
|
||||||
try:
|
try:
|
||||||
category = random.choice(self.faction.building_set)
|
category = random.choice(self.faction.building_set)
|
||||||
@ -733,6 +753,33 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
g.groups = groups
|
g.groups = groups
|
||||||
self.control_point.connected_objectives.append(g)
|
self.control_point.connected_objectives.append(g)
|
||||||
|
|
||||||
|
def generate_ewr_site(self) -> None:
|
||||||
|
position = self.location_finder.location_for(LocationType.Ewr)
|
||||||
|
if position is None:
|
||||||
|
return
|
||||||
|
self.generate_ewr_at(position)
|
||||||
|
|
||||||
|
def generate_ewr_at(self, position: Point) -> None:
|
||||||
|
group_id = self.game.next_group_id()
|
||||||
|
|
||||||
|
g = EwrGroundObject(
|
||||||
|
namegen.random_objective_name(),
|
||||||
|
group_id,
|
||||||
|
position,
|
||||||
|
self.control_point,
|
||||||
|
for_airbase=False,
|
||||||
|
)
|
||||||
|
group = generate_ewr_group(self.game, g, self.faction)
|
||||||
|
if group is None:
|
||||||
|
logging.error(
|
||||||
|
"Could not generate ewr group for %s at %s",
|
||||||
|
g.name,
|
||||||
|
self.control_point,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
g.groups = [group]
|
||||||
|
self.control_point.connected_objectives.append(g)
|
||||||
|
|
||||||
def generate_missile_sites(self) -> None:
|
def generate_missile_sites(self) -> None:
|
||||||
for i in range(self.faction.missiles_group_count):
|
for i in range(self.faction.missiles_group_count):
|
||||||
self.generate_missile_site()
|
self.generate_missile_site()
|
||||||
|
|||||||
@ -442,7 +442,12 @@ class VehicleGroupGroundObject(BaseDefenseGroundObject):
|
|||||||
|
|
||||||
class EwrGroundObject(BaseDefenseGroundObject):
|
class EwrGroundObject(BaseDefenseGroundObject):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name: str, group_id: int, position: Point, control_point: ControlPoint
|
self,
|
||||||
|
name: str,
|
||||||
|
group_id: int,
|
||||||
|
position: Point,
|
||||||
|
control_point: ControlPoint,
|
||||||
|
for_airbase: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
name=name,
|
name=name,
|
||||||
@ -452,7 +457,7 @@ class EwrGroundObject(BaseDefenseGroundObject):
|
|||||||
heading=0,
|
heading=0,
|
||||||
control_point=control_point,
|
control_point=control_point,
|
||||||
dcs_identifier="EWR",
|
dcs_identifier="EWR",
|
||||||
airbase_group=True,
|
airbase_group=for_airbase,
|
||||||
sea_object=False,
|
sea_object=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
63
gen/sam/ewr_group_generator.py
Normal file
63
gen/sam/ewr_group_generator.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import random
|
||||||
|
from typing import List, Optional, Type
|
||||||
|
|
||||||
|
from dcs.unitgroup import VehicleGroup
|
||||||
|
|
||||||
|
from game import Game
|
||||||
|
from game.factions.faction import Faction
|
||||||
|
from game.theater.theatergroundobject import EwrGroundObject
|
||||||
|
from gen.sam.ewrs import (
|
||||||
|
BigBirdGenerator,
|
||||||
|
BoxSpringGenerator,
|
||||||
|
DogEarGenerator,
|
||||||
|
FlatFaceGenerator,
|
||||||
|
HawkEwrGenerator,
|
||||||
|
PatriotEwrGenerator,
|
||||||
|
RolandEwrGenerator,
|
||||||
|
SnowDriftGenerator,
|
||||||
|
StraightFlushGenerator,
|
||||||
|
TallRackGenerator,
|
||||||
|
)
|
||||||
|
from gen.sam.group_generator import GroupGenerator
|
||||||
|
|
||||||
|
EWR_MAP = {
|
||||||
|
"BoxSpringGenerator": BoxSpringGenerator,
|
||||||
|
"TallRackGenerator": TallRackGenerator,
|
||||||
|
"DogEarGenerator": DogEarGenerator,
|
||||||
|
"RolandEwrGenerator": RolandEwrGenerator,
|
||||||
|
"FlatFaceGenerator": FlatFaceGenerator,
|
||||||
|
"PatriotEwrGenerator": PatriotEwrGenerator,
|
||||||
|
"BigBirdGenerator": BigBirdGenerator,
|
||||||
|
"SnowDriftGenerator": SnowDriftGenerator,
|
||||||
|
"StraightFlushGenerator": StraightFlushGenerator,
|
||||||
|
"HawkEwrGenerator": HawkEwrGenerator,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_faction_possible_ewrs_generator(
|
||||||
|
faction: Faction,
|
||||||
|
) -> List[Type[GroupGenerator]]:
|
||||||
|
"""
|
||||||
|
Return the list of possible EWR generators for the given faction
|
||||||
|
:param faction: Faction name to search units for
|
||||||
|
"""
|
||||||
|
return [EWR_MAP[s] for s in faction.ewrs]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_ewr_group(
|
||||||
|
game: Game, ground_object: EwrGroundObject, faction: Faction
|
||||||
|
) -> Optional[VehicleGroup]:
|
||||||
|
"""Generates an early warning radar group.
|
||||||
|
|
||||||
|
:param game: The Game.
|
||||||
|
:param ground_object: The ground object which will own the EWR group.
|
||||||
|
:param faction: Owner faction.
|
||||||
|
:return: The generated group, or None if one could not be generated.
|
||||||
|
"""
|
||||||
|
generators = get_faction_possible_ewrs_generator(faction)
|
||||||
|
if len(generators) > 0:
|
||||||
|
generator_class = random.choice(generators)
|
||||||
|
generator = generator_class(game, ground_object)
|
||||||
|
generator.generate()
|
||||||
|
return generator.get_generated_group()
|
||||||
|
return None
|
||||||
@ -23,18 +23,7 @@ from gen.sam.cold_war_flak import (
|
|||||||
ColdWarFlakGenerator,
|
ColdWarFlakGenerator,
|
||||||
EarlyColdWarFlakGenerator,
|
EarlyColdWarFlakGenerator,
|
||||||
)
|
)
|
||||||
from gen.sam.ewrs import (
|
|
||||||
BigBirdGenerator,
|
|
||||||
BoxSpringGenerator,
|
|
||||||
DogEarGenerator,
|
|
||||||
FlatFaceGenerator,
|
|
||||||
HawkEwrGenerator,
|
|
||||||
PatriotEwrGenerator,
|
|
||||||
RolandEwrGenerator,
|
|
||||||
SnowDriftGenerator,
|
|
||||||
StraightFlushGenerator,
|
|
||||||
TallRackGenerator,
|
|
||||||
)
|
|
||||||
from gen.sam.freya_ewr import FreyaGenerator
|
from gen.sam.freya_ewr import FreyaGenerator
|
||||||
from gen.sam.group_generator import GroupGenerator
|
from gen.sam.group_generator import GroupGenerator
|
||||||
from gen.sam.sam_avenger import AvengerGenerator
|
from gen.sam.sam_avenger import AvengerGenerator
|
||||||
@ -152,19 +141,6 @@ SAM_PRICES = {
|
|||||||
AirDefence.HQ_7_Self_Propelled_LN: 35,
|
AirDefence.HQ_7_Self_Propelled_LN: 35,
|
||||||
}
|
}
|
||||||
|
|
||||||
EWR_MAP = {
|
|
||||||
"BoxSpringGenerator": BoxSpringGenerator,
|
|
||||||
"TallRackGenerator": TallRackGenerator,
|
|
||||||
"DogEarGenerator": DogEarGenerator,
|
|
||||||
"RolandEwrGenerator": RolandEwrGenerator,
|
|
||||||
"FlatFaceGenerator": FlatFaceGenerator,
|
|
||||||
"PatriotEwrGenerator": PatriotEwrGenerator,
|
|
||||||
"BigBirdGenerator": BigBirdGenerator,
|
|
||||||
"SnowDriftGenerator": SnowDriftGenerator,
|
|
||||||
"StraightFlushGenerator": StraightFlushGenerator,
|
|
||||||
"HawkEwrGenerator": HawkEwrGenerator,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_faction_possible_sams_generator(
|
def get_faction_possible_sams_generator(
|
||||||
faction: Faction,
|
faction: Faction,
|
||||||
@ -176,14 +152,6 @@ def get_faction_possible_sams_generator(
|
|||||||
return [SAM_MAP[s] for s in faction.air_defenses]
|
return [SAM_MAP[s] for s in faction.air_defenses]
|
||||||
|
|
||||||
|
|
||||||
def get_faction_possible_ewrs_generator(faction: Faction) -> List[Type[GroupGenerator]]:
|
|
||||||
"""
|
|
||||||
Return the list of possible SAM generator for the given faction
|
|
||||||
:param faction: Faction name to search units for
|
|
||||||
"""
|
|
||||||
return [EWR_MAP[s] for s in faction.ewrs]
|
|
||||||
|
|
||||||
|
|
||||||
def _generate_anti_air_from(
|
def _generate_anti_air_from(
|
||||||
generators: Sequence[Type[AirDefenseGroupGenerator]],
|
generators: Sequence[Type[AirDefenseGroupGenerator]],
|
||||||
game: Game,
|
game: Game,
|
||||||
@ -236,22 +204,3 @@ def generate_anti_air_group(
|
|||||||
if groups:
|
if groups:
|
||||||
return groups
|
return groups
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def generate_ewr_group(
|
|
||||||
game: Game, ground_object: TheaterGroundObject, faction: Faction
|
|
||||||
) -> Optional[VehicleGroup]:
|
|
||||||
"""Generates an early warning radar group.
|
|
||||||
|
|
||||||
:param game: The Game.
|
|
||||||
:param ground_object: The ground object which will own the EWR group.
|
|
||||||
:param faction: Owner faction.
|
|
||||||
:return: The generated group, or None if one could not be generated.
|
|
||||||
"""
|
|
||||||
generators = get_faction_possible_ewrs_generator(faction)
|
|
||||||
if len(generators) > 0:
|
|
||||||
generator_class = random.choice(generators)
|
|
||||||
generator = generator_class(game, ground_object)
|
|
||||||
generator.generate()
|
|
||||||
return generator.get_generated_group()
|
|
||||||
return None
|
|
||||||
|
|||||||
@ -147,6 +147,8 @@ def load_icons():
|
|||||||
"./resources/ui/ground_assets/" + category + "_blue.png"
|
"./resources/ui/ground_assets/" + category + "_blue.png"
|
||||||
)
|
)
|
||||||
ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.png")
|
ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.png")
|
||||||
|
ICONS["EWR"] = QPixmap("./resources/ui/ground_assets/ewr.png")
|
||||||
|
ICONS["EWR_blue"] = QPixmap("./resources/ui/ground_assets/ewr_blue.png")
|
||||||
ICONS["ship"] = QPixmap("./resources/ui/ground_assets/ship.png")
|
ICONS["ship"] = QPixmap("./resources/ui/ground_assets/ship.png")
|
||||||
ICONS["ship_blue"] = QPixmap("./resources/ui/ground_assets/ship_blue.png")
|
ICONS["ship_blue"] = QPixmap("./resources/ui/ground_assets/ship_blue.png")
|
||||||
ICONS["missile"] = QPixmap("./resources/ui/ground_assets/missile.png")
|
ICONS["missile"] = QPixmap("./resources/ui/ground_assets/missile.png")
|
||||||
|
|||||||
Binary file not shown.
BIN
resources/ui/ground_assets/ewr.png
Normal file
BIN
resources/ui/ground_assets/ewr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/ui/ground_assets/ewr_blue.png
Normal file
BIN
resources/ui/ground_assets/ewr_blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 881 B |
Loading…
x
Reference in New Issue
Block a user