mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add support for additional EWR sites in campaigns.
* A Bluefor EWR 55GS in the campaign miz defines an optional EWR site. There is no distinction between how close or far it is to a base, so it's possible that there will be many EWRs within an airbase. * A Redfor EWR 1L13 in the campaign miz defines a required EWR site. It would be a good future idea to limit the amount of EWRs within a certain distance from an airbase. That way there's no chance of 5 EWRs all at the same airbase. Even better if there were something preventing any two EWRs from being right next to each other. No campaigns take advantage of this yet. Fixes https://github.com/Khopa/dcs_liberation/issues/524
This commit is contained in:
parent
7ce05762f5
commit
52ce1a5959
@ -118,6 +118,8 @@ class MizCampaignLoader:
|
|||||||
AirDefence.SAM_SA_3_S_125_LN_5P73.id,
|
AirDefence.SAM_SA_3_S_125_LN_5P73.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