Add support for required SAMs in campaigns.

"Required" SAMs (designative by redfor long range SAM launchers in the
ME) will always be spawned during campaign generation. This makes it
possible to build a semi-guaranteed IADS (the exact type of SAM is
dependent) on the choice of faction.

Requierd SAMs will consume the slots of random SAMs during generation.
Later we should differentiate between strategic SAMs like SA-10s and
tactical SAMs like SA-11s so we can fill in the medium range SAMs at
random locations among the fixed long range SAMs.
This commit is contained in:
Dan Albert 2020-11-19 23:47:07 -08:00
parent cecf611f91
commit bc3cd50a6c
4 changed files with 44 additions and 2 deletions

View File

@ -105,6 +105,20 @@ class MizCampaignLoader:
OFFSHORE_STRIKE_TARGET_UNIT_TYPE = Fortification.Oil_platform.id
SHIP_UNIT_TYPE = USS_Arleigh_Burke_IIa.id
# Multiple options for the required SAMs so campaign designers can more
# easily see the coverage of their IADS. Designers focused on campaigns that
# will primarily use SA-2s can place SA-2 launchers to ensure that they will
# have adequate coverage, and designers focused on campaigns that will
# primarily use SA-10s can do the same.
REQUIRED_SAM_UNIT_TYPES = {
AirDefence.SAM_Hawk_LN_M192,
AirDefence.SAM_Patriot_LN_M901,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
AirDefence.SAM_SA_10_S_300PS_LN_5P85D,
AirDefence.SAM_SA_2_LN_SM_90,
AirDefence.SAM_SA_3_S_125_LN_5P73,
}
BASE_DEFENSE_RADIUS = nm_to_meter(2)
def __init__(self, miz: Path, theater: ConflictTheater) -> None:
@ -207,6 +221,12 @@ class MizCampaignLoader:
if group.units[0].type == self.OFFSHORE_STRIKE_TARGET_UNIT_TYPE:
yield group
@property
def required_sams(self) -> Iterator[VehicleGroup]:
for group in self.red.vehicle_group:
if group.units[0].type == self.REQUIRED_SAM_UNIT_TYPES:
yield group
@cached_property
def control_points(self) -> Dict[int, ControlPoint]:
control_points = {}
@ -306,6 +326,10 @@ class MizCampaignLoader:
closest, distance = self.objective_info(group)
closest.preset_locations.ships.append(group.position)
for group in self.required_sams:
closest, distance = self.objective_info(group)
closest.preset_locations.required_sams.append(group.position)
def populate_theater(self) -> None:
for control_point in self.control_points.values():
self.theater.add_controlpoint(control_point)

View File

@ -85,7 +85,7 @@ class PresetLocations:
offshore_strike_locations: List[Point] = field(default_factory=list)
#: Locations of SAMs which should always be spawned.
fixed_sams: List[Point] = field(default_factory=list)
required_sams: List[Point] = field(default_factory=list)
@staticmethod
def _random_from(points: List[Point]) -> Optional[Point]:

View File

@ -544,15 +544,31 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
# Always generate at least one AA point.
self.generate_aa_site()
skip_sams = self.generate_required_aa()
# And between 2 and 7 other objectives.
amount = random.randrange(2, 7)
for i in range(amount):
# 1 in 4 additional objectives are AA.
if random.randint(0, 3) == 0:
self.generate_aa_site()
if skip_sams > 0:
skip_sams -= 1
else:
self.generate_aa_site()
else:
self.generate_ground_point()
def generate_required_aa(self) -> int:
"""Generates the AA sites that are required by the campaign.
Returns:
The number of AA sites that were generated.
"""
sams = self.control_point.preset_locations.required_sams
for position in sams:
self.generate_aa_at(position)
return len(sams)
def generate_ground_point(self) -> None:
try:
category = random.choice(self.faction.building_set)
@ -591,7 +607,9 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
position = self.location_finder.location_for(LocationType.Sam)
if position is None:
return
self.generate_aa_at(position)
def generate_aa_at(self, position: Point) -> None:
group_id = self.game.next_group_id()
g = SamGroundObject(namegen.random_objective_name(), group_id,