Added coastal defenses sites generator for Iran and China.

This commit is contained in:
Khopa 2021-02-05 00:15:06 +01:00
parent 643dd65113
commit 2a6e2d470d
23 changed files with 212 additions and 65 deletions

View File

@ -60,6 +60,9 @@ class Faction:
# Possible Missile site generators for this faction # Possible Missile site generators for this faction
missiles: List[str] = field(default_factory=list) missiles: List[str] = field(default_factory=list)
# Possible costal site generators for this faction
coastal_defenses: List[str] = field(default_factory=list)
# Required mods or asset packs # Required mods or asset packs
requirements: Dict[str, str] = field(default_factory=dict) requirements: Dict[str, str] = field(default_factory=dict)
@ -90,6 +93,9 @@ class Faction:
# How many missiles group should we try to generate per CP on startup for this faction # How many missiles group should we try to generate per CP on startup for this faction
missiles_group_count: int = field(default=1) missiles_group_count: int = field(default=1)
# How many coastal group should we try to generate per CP on startup for this faction
coastal_group_count: int = field(default=1)
# Whether this faction has JTAC access # Whether this faction has JTAC access
has_jtac: bool = field(default=False) has_jtac: bool = field(default=False)
@ -153,6 +159,7 @@ class Faction:
faction.air_defenses.extend(json.get("shorads", [])) faction.air_defenses.extend(json.get("shorads", []))
faction.missiles = json.get("missiles", []) faction.missiles = json.get("missiles", [])
faction.coastal_defenses = json.get("coastal_defenses", [])
faction.requirements = json.get("requirements", {}) faction.requirements = json.get("requirements", {})
faction.carrier_names = json.get("carrier_names", []) faction.carrier_names = json.get("carrier_names", [])
@ -173,6 +180,7 @@ class Faction:
faction.jtac_unit = None faction.jtac_unit = None
faction.navy_group_count = int(json.get("navy_group_count", 1)) faction.navy_group_count = int(json.get("navy_group_count", 1))
faction.missiles_group_count = int(json.get("missiles_group_count", 0)) faction.missiles_group_count = int(json.get("missiles_group_count", 0))
faction.coastal_group_count = int(json.get("coastal_group_count", 0))
# Load doctrine # Load doctrine
doctrine = json.get("doctrine", "modern") doctrine = json.get("doctrine", "modern")

View File

@ -0,0 +1,16 @@
from dcs import Point
class PointWithHeading(Point):
def __init__(self):
super(PointWithHeading, self).__init__(0, 0)
self.heading = 0
@staticmethod
def from_point(point: Point, heading: int):
p = PointWithHeading()
p.x = point.x
p.y = point.y
p.heading = heading
return p

View File

@ -55,6 +55,7 @@ from .controlpoint import (
Fob, Fob,
) )
from .landmap import Landmap, load_landmap, poly_contains from .landmap import Landmap, load_landmap, poly_contains
from ..point_with_heading import PointWithHeading
from ..utils import Distance, meters, nautical_miles from ..utils import Distance, meters, nautical_miles
Numeric = Union[int, float] Numeric = Union[int, float]
@ -71,6 +72,7 @@ IMPORTANCE_HIGH = 1.4
FRONTLINE_MIN_CP_DISTANCE = 5000 FRONTLINE_MIN_CP_DISTANCE = 5000
def pairwise(iterable): def pairwise(iterable):
""" """
itertools recipe itertools recipe
@ -297,7 +299,7 @@ class MizCampaignLoader:
# final waypoint at the destination CP. Intermediate waypoints # final waypoint at the destination CP. Intermediate waypoints
# define the curve of the front line. # define the curve of the front line.
waypoints = [p.position for p in group.points] waypoints = [p.position for p in group.points]
origin = self.theater.closest_control_point(waypoints[0]) origin = self.theater.closest_control_point(waypoints[0])
if origin is None: if origin is None:
raise RuntimeError( raise RuntimeError(
f"No control point near the first waypoint of {group.name}") f"No control point near the first waypoint of {group.name}")
@ -326,7 +328,8 @@ class MizCampaignLoader:
for group in self.garrisons: for group in self.garrisons:
closest, distance = self.objective_info(group) closest, distance = self.objective_info(group)
if distance < self.BASE_DEFENSE_RADIUS: if distance < self.BASE_DEFENSE_RADIUS:
closest.preset_locations.base_garrisons.append(group.position) closest.preset_locations.base_garrisons.append(
PointWithHeading.from_point(group.position, group.units[0].heading))
else: else:
logging.warning( logging.warning(
f"Found garrison unit too far from base: {group.name}") f"Found garrison unit too far from base: {group.name}")
@ -334,42 +337,44 @@ class MizCampaignLoader:
for group in self.sams: for group in self.sams:
closest, distance = self.objective_info(group) closest, distance = self.objective_info(group)
if distance < self.BASE_DEFENSE_RADIUS: if distance < self.BASE_DEFENSE_RADIUS:
closest.preset_locations.base_air_defense.append(group.position) closest.preset_locations.base_air_defense.append(
PointWithHeading.from_point(group.position, group.units[0].heading))
else: else:
closest.preset_locations.strike_locations.append(group.position) closest.preset_locations.strike_locations.append(
PointWithHeading.from_point(group.position, group.units[0].heading))
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(group.position) 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)
closest.preset_locations.offshore_strike_locations.append( closest.preset_locations.offshore_strike_locations.append(
group.position) PointWithHeading.from_point(group.position, group.units[0].heading))
for group in self.ships: for group in self.ships:
closest, distance = self.objective_info(group) closest, distance = self.objective_info(group)
closest.preset_locations.ships.append(group.position) closest.preset_locations.ships.append(PointWithHeading.from_point(group.position, group.units[0].heading))
for group in self.missile_sites: for group in self.missile_sites:
closest, distance = self.objective_info(group) closest, distance = self.objective_info(group)
closest.preset_locations.missile_sites.append(group.position) closest.preset_locations.missile_sites.append(
PointWithHeading.from_point(group.position, group.units[0].heading))
for group in self.coastal_defenses: for group in self.coastal_defenses:
closest, distance = self.objective_info(group) closest, distance = self.objective_info(group)
closest.preset_locations.coastal_defenses.append(group.position) closest.preset_locations.coastal_defenses.append(
PointWithHeading.from_point(group.position, group.units[0].heading))
for group in self.required_long_range_sams: for group in self.required_long_range_sams:
closest, distance = self.objective_info(group) closest, distance = self.objective_info(group)
closest.preset_locations.required_long_range_sams.append( closest.preset_locations.required_long_range_sams.append(
group.position PointWithHeading.from_point(group.position, group.units[0].heading))
)
for group in self.required_medium_range_sams: for group in self.required_medium_range_sams:
closest, distance = self.objective_info(group) closest, distance = self.objective_info(group)
closest.preset_locations.required_medium_range_sams.append( closest.preset_locations.required_medium_range_sams.append(
group.position PointWithHeading.from_point(group.position, group.units[0].heading))
)
def populate_theater(self) -> None: def populate_theater(self) -> None:
for control_point in self.control_points.values(): for control_point in self.control_points.values():
@ -486,8 +491,8 @@ class ConflictTheater:
for inclusion_zone in self.landmap.inclusion_zones: for inclusion_zone in self.landmap.inclusion_zones:
nearest_pair = ops.nearest_points(point, inclusion_zone) nearest_pair = ops.nearest_points(point, inclusion_zone)
nearest_points.append(nearest_pair[1]) nearest_points.append(nearest_pair[1])
min_distance = point.distance(nearest_points[0]) # type: geometry.Point min_distance = point.distance(nearest_points[0]) # type: geometry.Point
nearest_point = nearest_points[0] # type: geometry.Point nearest_point = nearest_points[0] # type: geometry.Point
for pt in nearest_points[1:]: for pt in nearest_points[1:]:
distance = point.distance(pt) distance = point.distance(pt)
if distance < min_distance: if distance < min_distance:
@ -572,7 +577,7 @@ class ConflictTheater:
self.find_control_point_by_id(i) self.find_control_point_by_id(i)
for i for i
in min(all_cp_min_distances, key=all_cp_min_distances.get) # type: ignore in min(all_cp_min_distances, key=all_cp_min_distances.get) # type: ignore
] # type: List[ControlPoint] ] # type: List[ControlPoint]
assert len(closest_opposing_cps) == 2 assert len(closest_opposing_cps) == 2
if closest_opposing_cps[0].captured: if closest_opposing_cps[0].captured:
return cast(Tuple[ControlPoint, ControlPoint], tuple(closest_opposing_cps)) return cast(Tuple[ControlPoint, ControlPoint], tuple(closest_opposing_cps))
@ -788,10 +793,10 @@ class FrontLine(MissionTarget):
""" """
def __init__( def __init__(
self, self,
control_point_a: ControlPoint, control_point_a: ControlPoint,
control_point_b: ControlPoint, control_point_b: ControlPoint,
theater: ConflictTheater theater: ConflictTheater
) -> None: ) -> None:
self.control_point_a = control_point_a self.control_point_a = control_point_a
self.control_point_b = control_point_b self.control_point_b = control_point_b
@ -876,7 +881,7 @@ class FrontLine(MissionTarget):
according to the current strength of each control point according to the current strength of each control point
""" """
total_strength = ( total_strength = (
self.control_point_a.base.strength + self.control_point_b.base.strength self.control_point_a.base.strength + self.control_point_b.base.strength
) )
if self.control_point_a.base.strength == 0: if self.control_point_a.base.strength == 0:
return self._adjust_for_min_dist(0) return self._adjust_for_min_dist(0)
@ -891,11 +896,11 @@ class FrontLine(MissionTarget):
constant of either end control point. constant of either end control point.
""" """
if (distance > self.attack_distance / 2) and ( if (distance > self.attack_distance / 2) and (
distance + FRONTLINE_MIN_CP_DISTANCE > self.attack_distance distance + FRONTLINE_MIN_CP_DISTANCE > self.attack_distance
): ):
distance = self.attack_distance - FRONTLINE_MIN_CP_DISTANCE distance = self.attack_distance - FRONTLINE_MIN_CP_DISTANCE
elif (distance < self.attack_distance / 2) and ( elif (distance < self.attack_distance / 2) and (
distance < FRONTLINE_MIN_CP_DISTANCE distance < FRONTLINE_MIN_CP_DISTANCE
): ):
distance = FRONTLINE_MIN_CP_DISTANCE distance = FRONTLINE_MIN_CP_DISTANCE
return distance return distance
@ -910,8 +915,8 @@ class FrontLine(MissionTarget):
) )
complex_frontlines = self.theater.frontline_data complex_frontlines = self.theater.frontline_data
if (complex_frontlines) and ( if (complex_frontlines) and (
(control_point_ids in complex_frontlines) (control_point_ids in complex_frontlines)
or (reversed_cp_ids in complex_frontlines) or (reversed_cp_ids in complex_frontlines)
): ):
# The frontline segments must be stored in the correct order for the distance algorithms to work. # The frontline segments must be stored in the correct order for the distance algorithms to work.
# The points in the frontline are ordered from the id before the | to the id after. # The points in the frontline are ordered from the id before the | to the id after.
@ -935,10 +940,9 @@ class FrontLine(MissionTarget):
) )
) )
@staticmethod @staticmethod
def load_json_frontlines( def load_json_frontlines(
theater: ConflictTheater theater: ConflictTheater
) -> Optional[Dict[str, ComplexFrontLine]]: ) -> Optional[Dict[str, ComplexFrontLine]]:
"""Load complex frontlines from json""" """Load complex frontlines from json"""
try: try:

View File

@ -4,7 +4,6 @@ import heapq
import itertools import itertools
import logging import logging
import random import random
import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
@ -28,6 +27,7 @@ from gen.ground_forces.combat_stance import CombatStance
from gen.runways import RunwayAssigner, RunwayData from gen.runways import RunwayAssigner, RunwayData
from .base import Base from .base import Base
from .missiontarget import MissionTarget from .missiontarget import MissionTarget
from game.point_with_heading import PointWithHeading
from .theatergroundobject import ( from .theatergroundobject import (
BaseDefenseGroundObject, BaseDefenseGroundObject,
EwrGroundObject, EwrGroundObject,
@ -71,44 +71,43 @@ class LocationType(Enum):
Shorad = "SHORAD" Shorad = "SHORAD"
StrikeTarget = "strike target" StrikeTarget = "strike target"
@dataclass @dataclass
class PresetLocations: class PresetLocations:
"""Defines the preset locations loaded from the campaign mission file.""" """Defines the preset locations loaded from the campaign mission file."""
#: Locations used for spawning ground defenses for bases. #: Locations used for spawning ground defenses for bases.
base_garrisons: List[Point] = field(default_factory=list) base_garrisons: List[PointWithHeading] = field(default_factory=list)
#: Locations used for spawning air defenses for bases. Used by SAMs, AAA, #: Locations used for spawning air defenses for bases. Used by SAMs, AAA,
#: and SHORADs. #: and SHORADs.
base_air_defense: List[Point] = field(default_factory=list) base_air_defense: List[PointWithHeading] = field(default_factory=list)
#: Locations used by EWRs. #: Locations used by EWRs.
ewrs: List[Point] = field(default_factory=list) ewrs: List[PointWithHeading] = field(default_factory=list)
#: Locations used by non-carrier ships. Carriers and LHAs are not random. #: Locations used by non-carrier ships. Carriers and LHAs are not random.
ships: List[Point] = field(default_factory=list) ships: List[PointWithHeading] = field(default_factory=list)
#: Locations used by coastal defenses. #: Locations used by coastal defenses.
coastal_defenses: List[Point] = field(default_factory=list) coastal_defenses: List[PointWithHeading] = field(default_factory=list)
#: Locations used by ground based strike objectives. #: Locations used by ground based strike objectives.
strike_locations: List[Point] = field(default_factory=list) strike_locations: List[PointWithHeading] = field(default_factory=list)
#: Locations used by offshore strike objectives. #: Locations used by offshore strike objectives.
offshore_strike_locations: List[Point] = field(default_factory=list) offshore_strike_locations: List[PointWithHeading] = field(default_factory=list)
#: Locations used by missile sites like scuds and V-2s. #: Locations used by missile sites like scuds and V-2s.
missile_sites: List[Point] = field(default_factory=list) missile_sites: List[PointWithHeading] = field(default_factory=list)
#: Locations of long range SAMs which should always be spawned. #: Locations of long range SAMs which should always be spawned.
required_long_range_sams: List[Point] = field(default_factory=list) required_long_range_sams: List[PointWithHeading] = field(default_factory=list)
#: 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[Point] = field(default_factory=list) required_medium_range_sams: List[PointWithHeading] = field(default_factory=list)
@staticmethod @staticmethod
def _random_from(points: List[Point]) -> Optional[Point]: 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."""
if not points: if not points:
return None return None
@ -116,7 +115,7 @@ class PresetLocations:
points.remove(point) points.remove(point)
return point return point
def random_for(self, location_type: LocationType) -> Optional[Point]: def random_for(self, location_type: LocationType) -> Optional[PointWithHeading]:
"""Returns a position suitable for the given location type. """Returns a position suitable for the given location type.
The location, if found, will be claimed by the caller and not available The location, if found, will be claimed by the caller and not available
@ -376,20 +375,18 @@ class ControlPoint(MissionTarget, ABC):
# TODO: Should be Airbase specific. # TODO: Should be Airbase specific.
def clear_base_defenses(self) -> None: def clear_base_defenses(self) -> None:
for base_defense in self.base_defenses: for base_defense in self.base_defenses:
p = PointWithHeading.from_point(base_defense.position, base_defense.heading)
if isinstance(base_defense, EwrGroundObject): if isinstance(base_defense, EwrGroundObject):
self.preset_locations.ewrs.append(base_defense.position) self.preset_locations.ewrs.append(p)
elif isinstance(base_defense, SamGroundObject): elif isinstance(base_defense, SamGroundObject):
self.preset_locations.base_air_defense.append( self.preset_locations.base_air_defense.append(p)
base_defense.position)
elif isinstance(base_defense, VehicleGroupGroundObject): elif isinstance(base_defense, VehicleGroupGroundObject):
self.preset_locations.base_garrisons.append( self.preset_locations.base_garrisons.append(p)
base_defense.position)
else: else:
logging.error( logging.error(
"Could not determine preset location type for " "Could not determine preset location type for "
f"{base_defense}. Assuming garrison type.") f"{base_defense}. Assuming garrison type.")
self.preset_locations.base_garrisons.append( self.preset_locations.base_garrisons.append(p)
base_defense.position)
self.base_defenses = [] self.base_defenses = []
def capture_equipment(self, game: Game) -> None: def capture_equipment(self, game: Game) -> None:

View File

@ -13,7 +13,7 @@ from dcs.vehicles import AirDefence
from game import Game, db from game import Game, db
from game.factions.faction import Faction from game.factions.faction import Faction
from game.theater import Carrier, Lha, LocationType from game.theater import Carrier, Lha, LocationType, PointWithHeading
from game.theater.theatergroundobject import ( from game.theater.theatergroundobject import (
BuildingGroundObject, BuildingGroundObject,
CarrierGroundObject, CarrierGroundObject,
@ -22,10 +22,11 @@ from game.theater.theatergroundobject import (
MissileSiteGroundObject, MissileSiteGroundObject,
SamGroundObject, SamGroundObject,
ShipGroundObject, ShipGroundObject,
VehicleGroupGroundObject, VehicleGroupGroundObject, CoastalSiteGroundObject,
) )
from game.version import VERSION from game.version import VERSION
from gen import namegen from gen import namegen
from gen.coastal.coastal_group_generator import generate_coastal_group
from gen.defenses.armor_group_generator import generate_armor_group from gen.defenses.armor_group_generator import generate_armor_group
from gen.fleet.ship_group_generator import ( from gen.fleet.ship_group_generator import (
generate_carrier_group, generate_carrier_group,
@ -142,12 +143,12 @@ class LocationFinder:
self.miz_data = MizDataLocationFinder.compute_possible_locations( self.miz_data = MizDataLocationFinder.compute_possible_locations(
game.theater.terrain.name, control_point.full_name) game.theater.terrain.name, control_point.full_name)
def location_for(self, location_type: LocationType) -> Optional[Point]: def location_for(self, location_type: LocationType) -> Optional[PointWithHeading]:
position = self.control_point.preset_locations.random_for(location_type) position = self.control_point.preset_locations.random_for(location_type)
if position is not None: if position is not None:
return position return position
logging.warning(f"No campaign location for %s at %s", logging.warning(f"No campaign location for %s Mat %s",
location_type.value, self.control_point) location_type.value, self.control_point)
position = self.random_from_miz_data( position = self.random_from_miz_data(
location_type == LocationType.OffshoreStrikeTarget) location_type == LocationType.OffshoreStrikeTarget)
@ -164,7 +165,7 @@ class LocationFinder:
location_type.value, self.control_point) location_type.value, self.control_point)
return None return None
def random_from_miz_data(self, offshore: bool) -> Optional[Point]: def random_from_miz_data(self, offshore: bool) -> Optional[PointWithHeading]:
if offshore: if offshore:
locations = self.miz_data.offshore_locations locations = self.miz_data.offshore_locations
else: else:
@ -172,11 +173,16 @@ class LocationFinder:
if self.miz_data.offshore_locations: if self.miz_data.offshore_locations:
preset = random.choice(locations) preset = random.choice(locations)
locations.remove(preset) locations.remove(preset)
return preset.position return PointWithHeading.from_point(preset.position, preset.heading)
return None return None
def random_position(self, location_type: LocationType) -> Optional[Point]: def random_position(self, location_type: LocationType) -> Optional[PointWithHeading]:
# TODO: Flesh out preset locations so we never hit this case. # TODO: Flesh out preset locations so we never hit this case.
if location_type == LocationType.Coastal:
# No coastal locations generated randomly
return None
logging.warning("Falling back to random location for %s at %s", logging.warning("Falling back to random location for %s at %s",
location_type.value, self.control_point) location_type.value, self.control_point)
@ -228,7 +234,7 @@ class LocationFinder:
def _find_random_position(self, min_range: int, max_range: int, def _find_random_position(self, min_range: int, max_range: int,
on_ground: bool, is_base_defense: bool, on_ground: bool, is_base_defense: bool,
avoid_others: bool) -> Optional[Point]: avoid_others: bool) -> Optional[PointWithHeading]:
""" """
Find a valid ground object location Find a valid ground object location
:param on_ground: Whether it should be on ground or on sea (True = on :param on_ground: Whether it should be on ground or on sea (True = on
@ -241,7 +247,7 @@ class LocationFinder:
near = self.control_point.position near = self.control_point.position
others = self.control_point.ground_objects others = self.control_point.ground_objects
def is_valid(point: Optional[Point]) -> bool: def is_valid(point: Optional[PointWithHeading]) -> bool:
if point is None: if point is None:
return False return False
@ -272,9 +278,9 @@ class LocationFinder:
for _ in range(300): for _ in range(300):
# Check if on land or sea # Check if on land or sea
p = near.random_point_within(max_range, min_range) p = PointWithHeading.from_point(near.random_point_within(max_range, min_range), random.randint(0, 360))
if is_valid(p): if is_valid(p):
return p return
return None return None
@ -544,6 +550,9 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
if self.faction.missiles: if self.faction.missiles:
self.generate_missile_sites() self.generate_missile_sites()
if self.faction.coastal_defenses:
self.generate_coastal_sites()
return True return True
def generate_ground_points(self) -> None: def generate_ground_points(self) -> None:
@ -668,6 +677,26 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
self.control_point.connected_objectives.append(g) self.control_point.connected_objectives.append(g)
return return
def generate_coastal_sites(self) -> None:
for i in range(self.faction.coastal_group_count):
self.generate_coastal_site()
def generate_coastal_site(self) -> None:
position = self.location_finder.location_for(LocationType.Coastal)
if position is None:
return
group_id = self.game.next_group_id()
g = CoastalSiteGroundObject(namegen.random_objective_name(), group_id,
position, self.control_point, position.heading)
group = generate_coastal_group(self.game, g, self.faction_name)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
return
class FobGroundObjectGenerator(AirbaseGroundObjectGenerator): class FobGroundObjectGenerator(AirbaseGroundObjectGenerator):
def generate(self) -> bool: def generate(self) -> bool:

View File

@ -309,6 +309,23 @@ class MissileSiteGroundObject(TheaterGroundObject):
) )
class CoastalSiteGroundObject(TheaterGroundObject):
def __init__(self, name: str, group_id: int, position: Point,
control_point: ControlPoint, heading) -> None:
super().__init__(
name=name,
category="aa",
group_id=group_id,
position=position,
heading=heading,
control_point=control_point,
dcs_identifier="AA",
airbase_group=False,
sea_object=False
)
class BaseDefenseGroundObject(TheaterGroundObject): class BaseDefenseGroundObject(TheaterGroundObject):
"""Base type for all base defenses.""" """Base type for all base defenses."""

View File

@ -0,0 +1,27 @@
import logging
import random
from game import db
from gen.coastal.silkworm import SilkwormGenerator
COASTAL_MAP = {
"SilkwormGenerator": SilkwormGenerator,
}
def generate_coastal_group(game, ground_object, faction_name: str):
"""
This generate a coastal defenses group
:return: Nothing, but put the group reference inside the ground object
"""
faction = db.FACTIONS[faction_name]
if len(faction.coastal_defenses) > 0:
generators = faction.coastal_defenses
if len(generators) > 0:
gen = random.choice(generators)
if gen in COASTAL_MAP.keys():
generator = COASTAL_MAP[gen](game, ground_object, faction)
generator.generate()
return generator.get_generated_group()
else:
logging.info("Unable to generate missile group, generator : " + str(gen) + "does not exists")
return None

32
gen/coastal/silkworm.py Normal file
View File

@ -0,0 +1,32 @@
from dcs.vehicles import MissilesSS, Unarmed, AirDefence
from gen.sam.group_generator import GroupGenerator
class SilkwormGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(SilkwormGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
positions = self.get_circular_position(3, launcher_distance=120, coverage=180)
self.add_unit(MissilesSS.Silkworm_Radar, "SR#0", self.position.x, self.position.y, self.heading)
# Launchers
for i, p in enumerate(positions):
self.add_unit(MissilesSS.SS_N_2_Silkworm, "Missile#" + str(i), p[0], p[1], self.heading)
# Commander
self.add_unit(Unarmed.Transport_UAZ_469, "UAZ#0", self.position.x - 35, self.position.y - 20,
self.heading)
# Shorad
self.add_unit(AirDefence.SPAAA_ZSU_23_4_Shilka, "SHILKA#0", self.position.x - 55, self.position.y - 38,
self.heading)
# Shorad 2
self.add_unit(AirDefence.SAM_SA_9_Strela_1_9P31, "STRELA#0",
self.position.x + 200, self.position.y + 15, 90)

View File

@ -39,6 +39,7 @@ def generate_ship_group(game, ground_object, faction_name: str):
gen = random.choice(faction.navy_generators) gen = random.choice(faction.navy_generators)
if gen in SHIP_MAP.keys(): if gen in SHIP_MAP.keys():
generator = SHIP_MAP[gen](game, ground_object, faction) generator = SHIP_MAP[gen](game, ground_object, faction)
print(generator.position)
generator.generate() generator.generate()
return generator.get_generated_group() return generator.get_generated_group()
else: else:

View File

@ -12,7 +12,7 @@ MISSILES_MAP = {
def generate_missile_group(game, ground_object, faction_name: str): def generate_missile_group(game, ground_object, faction_name: str):
""" """
This generate a ship group This generate a missiles group
:return: Nothing, but put the group reference inside the ground object :return: Nothing, but put the group reference inside the ground object
""" """
faction = db.FACTIONS[faction_name] faction = db.FACTIONS[faction_name]

View File

@ -134,6 +134,8 @@ def load_icons():
ICONS["missile_blue"] = QPixmap("./resources/ui/ground_assets/missile_blue.png") ICONS["missile_blue"] = QPixmap("./resources/ui/ground_assets/missile_blue.png")
ICONS["nothreat"] = QPixmap("./resources/ui/ground_assets/nothreat.png") ICONS["nothreat"] = QPixmap("./resources/ui/ground_assets/nothreat.png")
ICONS["nothreat_blue"] = QPixmap("./resources/ui/ground_assets/nothreat_blue.png") ICONS["nothreat_blue"] = QPixmap("./resources/ui/ground_assets/nothreat_blue.png")
ICONS["coastal"] = QPixmap("./resources/ui/ground_assets/coastal.png")
ICONS["coastal_blue"] = QPixmap("./resources/ui/ground_assets/coastal_blue.png")
ICONS["Generator"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/generator.png") ICONS["Generator"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/generator.png")
ICONS["Missile"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/missile.png") ICONS["Missile"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/missile.png")

View File

@ -9,7 +9,7 @@ from game import Game
from game.data.building_data import FORTIFICATION_BUILDINGS from game.data.building_data import FORTIFICATION_BUILDINGS
from game.db import REWARDS from game.db import REWARDS
from game.theater import ControlPoint, TheaterGroundObject from game.theater import ControlPoint, TheaterGroundObject
from game.theater.theatergroundobject import MissileSiteGroundObject from game.theater.theatergroundobject import MissileSiteGroundObject, CoastalSiteGroundObject
from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu
from .QMapObject import QMapObject from .QMapObject import QMapObject
from ...displayoptions import DisplayOptions from ...displayoptions import DisplayOptions
@ -77,6 +77,8 @@ class QMapGroundObject(QMapObject):
cat = "ship" cat = "ship"
if isinstance(self.ground_object, MissileSiteGroundObject): if isinstance(self.ground_object, MissileSiteGroundObject):
cat = "missile" cat = "missile"
if isinstance(self.ground_object, CoastalSiteGroundObject):
cat = "coastal"
rect = QRect(option.rect.x() + 2, option.rect.y(), rect = QRect(option.rect.x() + 2, option.rect.y(),
option.rect.width() - 2, option.rect.height()) option.rect.width() - 2, option.rect.height())

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -57,6 +57,10 @@
"BoxSpringGenerator", "BoxSpringGenerator",
"TallRackGenerator" "TallRackGenerator"
], ],
"coastal_defenses": [
"SilkwormGenerator"
],
"coastal_group_count": 2,
"aircraft_carrier": [ "aircraft_carrier": [
"CV_1143_5_Admiral_Kuznetsov" "CV_1143_5_Admiral_Kuznetsov"
], ],

View File

@ -69,6 +69,10 @@
"ScudGenerator" "ScudGenerator"
], ],
"missiles_group_count": 1, "missiles_group_count": 1,
"coastal_defenses": [
"SilkwormGenerator"
],
"coastal_group_count": 2,
"navy_generators": [ "navy_generators": [
"GrishaGroupGenerator", "GrishaGroupGenerator",
"MolniyaGroupGenerator" "MolniyaGroupGenerator"

View File

@ -79,6 +79,10 @@
"ScudGenerator" "ScudGenerator"
], ],
"missiles_group_count": 1, "missiles_group_count": 1,
"coastal_defenses": [
"SilkwormGenerator"
],
"coastal_group_count": 3,
"navy_generators": [ "navy_generators": [
"GrishaGroupGenerator", "GrishaGroupGenerator",
"MolniyaGroupGenerator" "MolniyaGroupGenerator"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB