Ejemplo n.º 1
0
def generateGroup(mission: Mission, flight: FlightData,
                  package: PackageHandler):
    template = getTemplateGroup(mission, flight)

    if len(flight.members) == 0:
        return
    if len(flight.members) > 4:
        raise Exception("Too many units in flight, limit is 4")
    group = createGroup(mission, flight)
    country = mission.country("USA")

    group.task = flight.role
    parkingHandler = ParkingHandler(mission, template, country)
    for i, member in enumerate(flight.members):
        aircraft_type = readAircraftType(member)
        p = mission.aircraft(member.unitName(), aircraft_type, country)
        parkingHandler.assign_parking(p)
        p.set_client()
        package.setupRadio(p, flight.frequency)
        group.add_unit(p)

    mission.flight_group_from_airport
    assignFromTemplate(group, template)
    country.add_aircraft_group(group)
    return
Ejemplo n.º 2
0
def getTemplateGroup(mission: Mission, flight: FlightData):
    template_group = mission.find_group(flight.callsign)
    if not template_group:
        raise Exception(
            f"Could not find template for group {flight.callsign} in mission {mission.filename}"
        )
    mission.remove_plane_group(template_group)
    return template_group
Ejemplo n.º 3
0
def load_files(files) -> List[Mission]:
    missions = []
    for file in files:
        if validate_miz(file):
            mission = Mission()
            mission.load_file(file)
            missions.append(mission)
        else:
            print(f"Error: {file} doesn't look like a valid mission file.")
    return missions
Ejemplo n.º 4
0
    def __init__(self, miz: Path, theater: ConflictTheater) -> None:
        self.theater = theater
        self.mission = Mission()
        self.mission.load_file(str(miz))
        self.control_point_id = itertools.count(1000)

        # If there are no red carriers there usually aren't red units. Make sure
        # both countries are initialized so we don't have to deal with None.
        if self.mission.country(self.BLUE_COUNTRY.name) is None:
            self.mission.coalition["blue"].add_country(self.BLUE_COUNTRY)
        if self.mission.country(self.RED_COUNTRY.name) is None:
            self.mission.coalition["red"].add_country(self.RED_COUNTRY)
Ejemplo n.º 5
0
    def __init__(self, miz: Path, theater: ConflictTheater) -> None:
        self.theater = theater
        self.mission = Mission()
        with logged_duration("Loading miz"):
            self.mission.load_file(str(miz))

        # If there are no red carriers there usually aren't red units. Make sure
        # both countries are initialized so we don't have to deal with None.
        if self.mission.country(self.BLUE_COUNTRY.name) is None:
            self.mission.coalition["blue"].add_country(self.BLUE_COUNTRY)
        if self.mission.country(self.RED_COUNTRY.name) is None:
            self.mission.coalition["red"].add_country(self.RED_COUNTRY)
Ejemplo n.º 6
0
def createGroup(
    mission: Mission, flight: FlightData
) -> Union[unitgroup.PlaneGroup, unitgroup.HelicopterGroup]:
    aircraft_types = [readAircraftType(member) for member in flight.members]
    if all(aircraft_type.helicopter for aircraft_type in aircraft_types):
        return mission.helicopter_group(flight.callsign)
    elif not any(aircraft_type.helicopter for aircraft_type in aircraft_types):
        return mission.plane_group(flight.callsign)
    else:
        raise Exception(
            "A group must consist of all the same types: planes or helicopters"
        )
    def compute_possible_locations(
            terrain_name: str, cp_name: str) -> PresetControlPointLocations:
        """
        Extract the list of preset locations from miz data
        :param terrain_name: Terrain/Map name
        :param cp_name: Control Point / Airbase name
        :return:
        """

        miz_file = Path("./resources/mizdata/", terrain_name.lower(),
                        cp_name + ".miz")

        offshore_locations: List[PresetLocation] = []
        ashore_locations: List[PresetLocation] = []
        powerplants_locations: List[PresetLocation] = []
        antiship_locations: List[PresetLocation] = []

        if miz_file.exists():
            m = Mission()
            m.load_file(miz_file.absolute())

            for vehicle_group in m.country("USA").vehicle_group:
                if len(vehicle_group.units) > 0:
                    ashore_locations.append(
                        PresetLocation(vehicle_group.position,
                                       vehicle_group.units[0].heading,
                                       vehicle_group.name))

            for ship_group in m.country("USA").ship_group:
                if len(ship_group.units) > 0 and ship_group.units[
                        0].type == ships.Oliver_Hazzard_Perry_class.id:
                    offshore_locations.append(
                        PresetLocation(ship_group.position,
                                       ship_group.units[0].heading,
                                       ship_group.name))

            for static_group in m.country("USA").static_group:
                if len(static_group.units) > 0:
                    powerplants_locations.append(
                        PresetLocation(static_group.position,
                                       static_group.units[0].heading,
                                       static_group.name))

            if m.country("Iran") is not None:
                for vehicle_group in m.country("Iran").vehicle_group:
                    if len(vehicle_group.units) > 0 and vehicle_group.units[
                            0].type == MissilesSS.SS_N_2_Silkworm.id:
                        antiship_locations.append(
                            PresetLocation(vehicle_group.position,
                                           vehicle_group.units[0].heading,
                                           vehicle_group.name))

        return PresetControlPointLocations(ashore_locations,
                                           offshore_locations,
                                           antiship_locations,
                                           powerplants_locations)
Ejemplo n.º 8
0
 def prepare(cls, game: Game) -> None:
     with open("resources/default_options.lua", "r") as f:
         options_dict = loads(f.read())["options"]
     cls._set_mission(Mission(game.theater.terrain))
     cls.game = game
     cls._setup_mission_coalitions()
     cls.current_mission.options.load_from_dict(options_dict)
Ejemplo n.º 9
0
def create_mission(terrain: Terrain) -> Path:
    m = Mission(terrain)

    json_trigger = TriggerStart(comment=f"Load JSON")
    json_lua = m.map_resource.add_resource_file(JSON_LUA)
    json_trigger.add_action(DoScriptFile(json_lua))
    m.triggerrules.triggers.append(json_trigger)

    export_trigger = TriggerStart(comment=f"Load coordinate export")
    export_lua = m.map_resource.add_resource_file(EXPORT_LUA)
    export_trigger.add_action(DoScriptFile(export_lua))
    m.triggerrules.triggers.append(export_trigger)

    mission_path = DCS_SAVED_GAMES / "Missions" / f"export_{terrain.name.lower()}.miz"
    m.save(mission_path)
    return mission_path
Ejemplo n.º 10
0
    def prepare(self, terrain: Terrain, is_quick: bool):
        with open("resources/default_options.lua", "r") as f:
            options_dict = loads(f.read())["options"]

        self.current_mission = Mission(terrain)

        print(self.game.player_country)
        print(country_dict[db.country_id_from_name(self.game.player_country)])
        print(country_dict[db.country_id_from_name(
            self.game.player_country)]())

        # Setup coalition :
        self.current_mission.coalition["blue"] = Coalition("blue")
        self.current_mission.coalition["red"] = Coalition("red")

        p_country = self.game.player_country
        e_country = self.game.enemy_country
        self.current_mission.coalition["blue"].add_country(
            country_dict[db.country_id_from_name(p_country)]())
        self.current_mission.coalition["red"].add_country(
            country_dict[db.country_id_from_name(e_country)]())

        print([
            c for c in self.current_mission.coalition["blue"].countries.keys()
        ])
        print([
            c for c in self.current_mission.coalition["red"].countries.keys()
        ])

        if is_quick:
            self.quick_mission = self.current_mission
        else:
            self.regular_mission = self.current_mission

        self.current_mission.options.load_from_dict(options_dict)
        self.is_quick = is_quick

        if is_quick:
            self.attackers_starting_position = None
            self.defenders_starting_position = None
        else:
            self.attackers_starting_position = self.departure_cp.at
            # TODO: Is this possible?
            if self.to_cp is not None:
                self.defenders_starting_position = self.to_cp.at
            else:
                self.defenders_starting_position = None
Ejemplo n.º 11
0
    def __init__(self, game: Game, time: datetime) -> None:
        self.game = game
        self.time = time
        self.mission = Mission(game.theater.terrain)
        self.unit_map = UnitMap()

        self.air_support = AirSupport()

        self.laser_code_registry = LaserCodeRegistry()
        self.radio_registry = RadioRegistry()
        self.tacan_registry = TacanRegistry()

        self.generation_started = False

        with open("resources/default_options.lua", "r", encoding="utf-8") as f:
            self.mission.options.load_from_dict(
                dcs.lua.loads(f.read())["options"])
Ejemplo n.º 12
0
def create_frontline_dict(mission: Mission) -> Dict[str, Dict]:
    frontline_dict = {}
    for group in mission.country("USA").vehicle_group:
        groupname = str(group.name).replace(group.name.id, "").replace(":","")
        control_points = groupname.split("|")
        frontline_dict[groupname] = {
            "points": [(i.position.x, i.position.y) for i in group.points],
            "start_cp": int(control_points[0])
            }
    return frontline_dict
Ejemplo n.º 13
0
def generateMission(sheetPath: str, templateMissionPath: str, outDir: str):
    sheet = pa.read_excel(sheetPath, sheet_name=signup,
                          engine="odf").to_numpy()
    flightData = loadFlightData(sheet)

    mission = Mission()
    mission.load_file(templateMissionPath)
    package = PackageHandler(sheet, mission)
    for flight in flightData:
        generateGroup(mission, flight, package)
    mission.save(f"{outDir}/{package.package_name}.miz")
Ejemplo n.º 14
0
class MizCampaignLoader:
    BLUE_COUNTRY = CombinedJointTaskForcesBlue()
    RED_COUNTRY = CombinedJointTaskForcesRed()

    OFF_MAP_UNIT_TYPE = F_15C.id

    CV_UNIT_TYPE = Stennis.id
    LHA_UNIT_TYPE = LHA_Tarawa.id
    FRONT_LINE_UNIT_TYPE = Armor.M_113.id
    SHIPPING_LANE_UNIT_TYPE = HandyWind.id

    FOB_UNIT_TYPE = Unarmed.SKP_11.id
    FARP_HELIPAD = "SINGLE_HELIPAD"

    OFFSHORE_STRIKE_TARGET_UNIT_TYPE = Fortification.Oil_platform.id
    SHIP_UNIT_TYPE = USS_Arleigh_Burke_IIa.id
    MISSILE_SITE_UNIT_TYPE = MissilesSS.Scud_B.id
    COASTAL_DEFENSE_UNIT_TYPE = MissilesSS.Hy_launcher.id

    # Multiple options for air defenses so campaign designers can more accurately see
    # the coverage of their IADS for the expected type.
    LONG_RANGE_SAM_UNIT_TYPES = {
        AirDefence.Patriot_ln.id,
        AirDefence.S_300PS_5P85C_ln.id,
        AirDefence.S_300PS_5P85D_ln.id,
    }

    MEDIUM_RANGE_SAM_UNIT_TYPES = {
        AirDefence.Hawk_ln.id,
        AirDefence.S_75M_Volhov.id,
        AirDefence._5p73_s_125_ln.id,
    }

    SHORT_RANGE_SAM_UNIT_TYPES = {
        AirDefence.M1097_Avenger.id,
        AirDefence.Rapier_fsa_launcher.id,
        AirDefence._2S6_Tunguska.id,
        AirDefence.Strela_1_9P31.id,
    }

    AAA_UNIT_TYPES = {
        AirDefence.Flak18.id,
        AirDefence.Vulcan.id,
        AirDefence.ZSU_23_4_Shilka.id,
    }

    EWR_UNIT_TYPE = AirDefence._1L13_EWR.id

    ARMOR_GROUP_UNIT_TYPE = Armor.M_1_Abrams.id

    FACTORY_UNIT_TYPE = Fortification.Workshop_A.id

    AMMUNITION_DEPOT_UNIT_TYPE = Warehouse._Ammunition_depot.id

    STRIKE_TARGET_UNIT_TYPE = Fortification.Tech_combine.id

    def __init__(self, miz: Path, theater: ConflictTheater) -> None:
        self.theater = theater
        self.mission = Mission()
        with logged_duration("Loading miz"):
            self.mission.load_file(str(miz))
        self.control_point_id = itertools.count(1000)

        # If there are no red carriers there usually aren't red units. Make sure
        # both countries are initialized so we don't have to deal with None.
        if self.mission.country(self.BLUE_COUNTRY.name) is None:
            self.mission.coalition["blue"].add_country(self.BLUE_COUNTRY)
        if self.mission.country(self.RED_COUNTRY.name) is None:
            self.mission.coalition["red"].add_country(self.RED_COUNTRY)

    @staticmethod
    def control_point_from_airport(airport: Airport) -> ControlPoint:

        # The wiki says this is a legacy property and to just use regular.
        size = SIZE_REGULAR

        # The importance is taken from the periodicity of the airport's
        # warehouse divided by 10. 30 is the default, and out of range (valid
        # values are between 1.0 and 1.4). If it is used, pick the default
        # importance.
        if airport.periodicity == 30:
            importance = IMPORTANCE_MEDIUM
        else:
            importance = airport.periodicity / 10

        cp = Airfield(airport, size, importance)
        cp.captured = airport.is_blue()

        # Use the unlimited aircraft option to determine if an airfield should
        # be owned by the player when the campaign is "inverted".
        cp.captured_invert = airport.unlimited_aircrafts

        return cp

    def country(self, blue: bool) -> Country:
        country = self.mission.country(
            self.BLUE_COUNTRY.name if blue else self.RED_COUNTRY.name)
        # Should be guaranteed because we initialized them.
        assert country
        return country

    @property
    def blue(self) -> Country:
        return self.country(blue=True)

    @property
    def red(self) -> Country:
        return self.country(blue=False)

    def off_map_spawns(self, blue: bool) -> Iterator[PlaneGroup]:
        for group in self.country(blue).plane_group:
            if group.units[0].type == self.OFF_MAP_UNIT_TYPE:
                yield group

    def carriers(self, blue: bool) -> Iterator[ShipGroup]:
        for group in self.country(blue).ship_group:
            if group.units[0].type == self.CV_UNIT_TYPE:
                yield group

    def lhas(self, blue: bool) -> Iterator[ShipGroup]:
        for group in self.country(blue).ship_group:
            if group.units[0].type == self.LHA_UNIT_TYPE:
                yield group

    def fobs(self, blue: bool) -> Iterator[VehicleGroup]:
        for group in self.country(blue).vehicle_group:
            if group.units[0].type == self.FOB_UNIT_TYPE:
                yield group

    @property
    def ships(self) -> Iterator[ShipGroup]:
        for group in self.red.ship_group:
            if group.units[0].type == self.SHIP_UNIT_TYPE:
                yield group

    @property
    def offshore_strike_targets(self) -> Iterator[StaticGroup]:
        for group in self.red.static_group:
            if group.units[0].type == self.OFFSHORE_STRIKE_TARGET_UNIT_TYPE:
                yield group

    @property
    def missile_sites(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[0].type == self.MISSILE_SITE_UNIT_TYPE:
                yield group

    @property
    def coastal_defenses(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[0].type == self.COASTAL_DEFENSE_UNIT_TYPE:
                yield group

    @property
    def long_range_sams(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[0].type in self.LONG_RANGE_SAM_UNIT_TYPES:
                yield group

    @property
    def medium_range_sams(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[0].type in self.MEDIUM_RANGE_SAM_UNIT_TYPES:
                yield group

    @property
    def short_range_sams(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[0].type in self.SHORT_RANGE_SAM_UNIT_TYPES:
                yield group

    @property
    def aaa(self) -> Iterator[VehicleGroup]:
        for group in itertools.chain(self.blue.vehicle_group,
                                     self.red.vehicle_group):
            if group.units[0].type in self.AAA_UNIT_TYPES:
                yield group

    @property
    def ewrs(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[0].type in self.EWR_UNIT_TYPE:
                yield group

    @property
    def armor_groups(self) -> Iterator[VehicleGroup]:
        for group in itertools.chain(self.blue.vehicle_group,
                                     self.red.vehicle_group):
            if group.units[0].type in self.ARMOR_GROUP_UNIT_TYPE:
                yield group

    @property
    def helipads(self) -> Iterator[StaticGroup]:
        for group in self.blue.static_group:
            if group.units[0].type == self.FARP_HELIPAD:
                yield group

    @property
    def factories(self) -> Iterator[StaticGroup]:
        for group in self.blue.static_group:
            if group.units[0].type in self.FACTORY_UNIT_TYPE:
                yield group

    @property
    def ammunition_depots(self) -> Iterator[StaticGroup]:
        for group in itertools.chain(self.blue.static_group,
                                     self.red.static_group):
            if group.units[0].type in self.AMMUNITION_DEPOT_UNIT_TYPE:
                yield group

    @property
    def strike_targets(self) -> Iterator[StaticGroup]:
        for group in itertools.chain(self.blue.static_group,
                                     self.red.static_group):
            if group.units[0].type in self.STRIKE_TARGET_UNIT_TYPE:
                yield group

    @property
    def scenery(self) -> List[SceneryGroup]:
        return SceneryGroup.from_trigger_zones(self.mission.triggers._zones)

    @cached_property
    def control_points(self) -> Dict[int, ControlPoint]:
        control_points = {}
        for airport in self.mission.terrain.airport_list():
            if airport.is_blue() or airport.is_red():
                control_point = self.control_point_from_airport(airport)
                control_points[control_point.id] = control_point

        for blue in (False, True):
            for group in self.off_map_spawns(blue):
                control_point = OffMapSpawn(next(self.control_point_id),
                                            str(group.name), group.position)
                control_point.captured = blue
                control_point.captured_invert = group.late_activation
                control_points[control_point.id] = control_point
            for ship in self.carriers(blue):
                # TODO: Name the carrier.
                control_point = Carrier("carrier", ship.position,
                                        next(self.control_point_id))
                control_point.captured = blue
                control_point.captured_invert = ship.late_activation
                control_points[control_point.id] = control_point
            for ship in self.lhas(blue):
                # TODO: Name the LHA.db
                control_point = Lha("lha", ship.position,
                                    next(self.control_point_id))
                control_point.captured = blue
                control_point.captured_invert = ship.late_activation
                control_points[control_point.id] = control_point
            for fob in self.fobs(blue):
                control_point = Fob(str(fob.name), fob.position,
                                    next(self.control_point_id))
                control_point.captured = blue
                control_point.captured_invert = fob.late_activation
                control_points[control_point.id] = control_point

        return control_points

    @property
    def front_line_path_groups(self) -> Iterator[VehicleGroup]:
        for group in self.country(blue=True).vehicle_group:
            if group.units[0].type == self.FRONT_LINE_UNIT_TYPE:
                yield group

    @property
    def shipping_lane_groups(self) -> Iterator[ShipGroup]:
        for group in self.country(blue=True).ship_group:
            if group.units[0].type == self.SHIPPING_LANE_UNIT_TYPE:
                yield group

    def add_supply_routes(self) -> None:
        for group in self.front_line_path_groups:
            # The unit will have its first waypoint at the source CP and the final
            # waypoint at the destination CP. Each waypoint defines the path of the
            # cargo ship.
            waypoints = [p.position for p in group.points]
            origin = self.theater.closest_control_point(waypoints[0])
            if origin is None:
                raise RuntimeError(
                    f"No control point near the first waypoint of {group.name}"
                )
            destination = self.theater.closest_control_point(waypoints[-1])
            if destination is None:
                raise RuntimeError(
                    f"No control point near the final waypoint of {group.name}"
                )

            self.control_points[origin.id].create_convoy_route(
                destination, waypoints)
            self.control_points[destination.id].create_convoy_route(
                origin, list(reversed(waypoints)))

    def add_shipping_lanes(self) -> None:
        for group in self.shipping_lane_groups:
            # The unit will have its first waypoint at the source CP and the final
            # waypoint at the destination CP. Each waypoint defines the path of the
            # cargo ship.
            waypoints = [p.position for p in group.points]
            origin = self.theater.closest_control_point(waypoints[0])
            if origin is None:
                raise RuntimeError(
                    f"No control point near the first waypoint of {group.name}"
                )
            destination = self.theater.closest_control_point(waypoints[-1])
            if destination is None:
                raise RuntimeError(
                    f"No control point near the final waypoint of {group.name}"
                )

            self.control_points[origin.id].create_shipping_lane(
                destination, waypoints)
            self.control_points[destination.id].create_shipping_lane(
                origin, list(reversed(waypoints)))

    def objective_info(self,
                       near: Positioned) -> Tuple[ControlPoint, Distance]:
        closest = self.theater.closest_control_point(near.position)
        distance = meters(closest.position.distance_to_point(near.position))
        return closest, distance

    def add_preset_locations(self) -> None:
        for static in self.offshore_strike_targets:
            closest, distance = self.objective_info(static)
            closest.preset_locations.offshore_strike_locations.append(
                PointWithHeading.from_point(static.position,
                                            static.units[0].heading))

        for ship in self.ships:
            closest, distance = self.objective_info(ship)
            closest.preset_locations.ships.append(
                PointWithHeading.from_point(ship.position,
                                            ship.units[0].heading))

        for group in self.missile_sites:
            closest, distance = self.objective_info(group)
            closest.preset_locations.missile_sites.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for group in self.coastal_defenses:
            closest, distance = self.objective_info(group)
            closest.preset_locations.coastal_defenses.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for group in self.long_range_sams:
            closest, distance = self.objective_info(group)
            closest.preset_locations.long_range_sams.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for group in self.medium_range_sams:
            closest, distance = self.objective_info(group)
            closest.preset_locations.medium_range_sams.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for group in self.short_range_sams:
            closest, distance = self.objective_info(group)
            closest.preset_locations.short_range_sams.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for group in self.aaa:
            closest, distance = self.objective_info(group)
            closest.preset_locations.aaa.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for group in self.ewrs:
            closest, distance = self.objective_info(group)
            closest.preset_locations.ewrs.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for group in self.armor_groups:
            closest, distance = self.objective_info(group)
            closest.preset_locations.armor_groups.append(
                PointWithHeading.from_point(group.position,
                                            group.units[0].heading))

        for static in self.helipads:
            closest, distance = self.objective_info(static)
            closest.helipads.append(
                PointWithHeading.from_point(static.position,
                                            static.units[0].heading))

        for static in self.factories:
            closest, distance = self.objective_info(static)
            closest.preset_locations.factories.append(
                PointWithHeading.from_point(static.position,
                                            static.units[0].heading))

        for static in self.ammunition_depots:
            closest, distance = self.objective_info(static)
            closest.preset_locations.ammunition_depots.append(
                PointWithHeading.from_point(static.position,
                                            static.units[0].heading))

        for static in self.strike_targets:
            closest, distance = self.objective_info(static)
            closest.preset_locations.strike_locations.append(
                PointWithHeading.from_point(static.position,
                                            static.units[0].heading))

        for scenery_group in self.scenery:
            closest, distance = self.objective_info(scenery_group)
            closest.preset_locations.scenery.append(scenery_group)

    def populate_theater(self) -> None:
        for control_point in self.control_points.values():
            self.theater.add_controlpoint(control_point)
        self.add_preset_locations()
        self.add_supply_routes()
        self.add_shipping_lanes()
Ejemplo n.º 15
0
class MissionGenerator:
    def __init__(self, game: Game, time: datetime) -> None:
        self.game = game
        self.time = time
        self.mission = Mission(game.theater.terrain)
        self.unit_map = UnitMap()

        self.air_support = AirSupport()

        self.laser_code_registry = LaserCodeRegistry()
        self.radio_registry = RadioRegistry()
        self.tacan_registry = TacanRegistry()

        self.generation_started = False

        with open("resources/default_options.lua", "r", encoding="utf-8") as f:
            self.mission.options.load_from_dict(
                dcs.lua.loads(f.read())["options"])

    def generate_miz(self, output: Path) -> UnitMap:
        if self.generation_started:
            raise RuntimeError(
                "Mission has already begun generating. To reset, create a new "
                "MissionSimulation.")
        self.generation_started = True

        self.setup_mission_coalitions()
        self.add_airfields_to_unit_map()
        self.initialize_registries()

        EnvironmentGenerator(self.mission, self.game.conditions,
                             self.time).generate()

        tgo_generator = TgoGenerator(
            self.mission,
            self.game,
            self.radio_registry,
            self.tacan_registry,
            self.unit_map,
        )
        tgo_generator.generate()

        ConvoyGenerator(self.mission, self.game, self.unit_map).generate()
        CargoShipGenerator(self.mission, self.game, self.unit_map).generate()

        self.generate_destroyed_units()

        # Generate ground conflicts first so the JTACs get the first laser code (1688)
        # rather than the first player flight with a TGP.
        self.generate_ground_conflicts()
        air_support, flights = self.generate_air_units(tgo_generator)

        TriggerGenerator(self.mission, self.game).generate()
        ForcedOptionsGenerator(self.mission, self.game).generate()
        VisualsGenerator(self.mission, self.game).generate()
        LuaGenerator(self.game, self.mission, air_support, flights).generate()
        DrawingsGenerator(self.mission, self.game).generate()

        self.setup_combined_arms()

        self.notify_info_generators(tgo_generator, air_support, flights)

        # TODO: Shouldn't this be first?
        namegen.reset_numbers()
        self.mission.save(output)

        return self.unit_map

    def setup_mission_coalitions(self) -> None:
        self.mission.coalition["blue"] = Coalition(
            "blue", bullseye=self.game.blue.bullseye.to_pydcs())
        self.mission.coalition["red"] = Coalition(
            "red", bullseye=self.game.red.bullseye.to_pydcs())
        self.mission.coalition["neutrals"] = Coalition(
            "neutrals",
            bullseye=Bullseye(Point(0, 0, self.mission.terrain)).to_pydcs())

        p_country = self.game.blue.country_name
        e_country = self.game.red.country_name
        self.mission.coalition["blue"].add_country(
            country_dict[country_id_from_name(p_country)]())
        self.mission.coalition["red"].add_country(
            country_dict[country_id_from_name(e_country)]())

        belligerents = [
            country_id_from_name(p_country),
            country_id_from_name(e_country),
        ]
        for country in country_dict.keys():
            if country not in belligerents:
                self.mission.coalition["neutrals"].add_country(
                    country_dict[country]())

    def add_airfields_to_unit_map(self) -> None:
        for control_point in self.game.theater.controlpoints:
            if isinstance(control_point, Airfield):
                self.unit_map.add_airfield(control_point)

    def initialize_registries(self) -> None:
        unique_map_frequencies: set[RadioFrequency] = set()
        self.initialize_tacan_registry(unique_map_frequencies)
        self.initialize_radio_registry(unique_map_frequencies)
        for frequency in unique_map_frequencies:
            self.radio_registry.reserve(frequency)

    def initialize_tacan_registry(
            self, unique_map_frequencies: set[RadioFrequency]) -> None:
        """
        Dedup beacon/radio frequencies, since some maps have some frequencies
        used multiple times.
        """
        beacons = load_beacons_for_terrain(self.game.theater.terrain.name)
        for beacon in beacons:
            unique_map_frequencies.add(beacon.frequency)
            if beacon.is_tacan:
                if beacon.channel is None:
                    logging.warning(
                        f"TACAN beacon has no channel: {beacon.callsign}")
                else:
                    self.tacan_registry.mark_unavailable(beacon.tacan_channel)

    def initialize_radio_registry(
            self, unique_map_frequencies: set[RadioFrequency]) -> None:
        for data in AirfieldData.for_theater(self.game.theater):
            if data.atc is not None:
                unique_map_frequencies.add(data.atc.hf)
                unique_map_frequencies.add(data.atc.vhf_fm)
                unique_map_frequencies.add(data.atc.vhf_am)
                unique_map_frequencies.add(data.atc.uhf)
                # No need to reserve ILS or TACAN because those are in the
                # beacon list.

    def generate_ground_conflicts(self) -> None:
        """Generate FLOTs and JTACs for each active front line."""
        for front_line in self.game.theater.conflicts():
            player_cp = front_line.blue_cp
            enemy_cp = front_line.red_cp
            conflict = FrontLineConflictDescription.frontline_cas_conflict(
                self.game.blue.faction.name,
                self.game.red.faction.name,
                self.mission.country(self.game.blue.country_name),
                self.mission.country(self.game.red.country_name),
                front_line,
                self.game.theater,
            )
            # Generate frontline ops
            player_gp = self.game.ground_planners[player_cp.id].units_per_cp[
                enemy_cp.id]
            enemy_gp = self.game.ground_planners[enemy_cp.id].units_per_cp[
                player_cp.id]
            ground_conflict_gen = FlotGenerator(
                self.mission,
                conflict,
                self.game,
                player_gp,
                enemy_gp,
                player_cp.stances[enemy_cp.id],
                enemy_cp.stances[player_cp.id],
                self.unit_map,
                self.radio_registry,
                self.air_support,
                self.laser_code_registry,
            )
            ground_conflict_gen.generate()

    def generate_air_units(
            self, tgo_generator: TgoGenerator
    ) -> tuple[AirSupport, list[FlightData]]:
        """Generate the air units for the Operation"""

        # Air Support (Tanker & Awacs)
        air_support_generator = AirSupportGenerator(
            self.mission,
            self.describe_air_conflict(),
            self.game,
            self.radio_registry,
            self.tacan_registry,
            self.air_support,
        )
        air_support_generator.generate()

        # Generate Aircraft Activity on the map
        aircraft_generator = AircraftGenerator(
            self.mission,
            self.game.settings,
            self.game,
            self.time,
            self.radio_registry,
            self.tacan_registry,
            self.laser_code_registry,
            self.unit_map,
            air_support=air_support_generator.air_support,
            helipads=tgo_generator.helipads,
        )

        aircraft_generator.clear_parking_slots()

        aircraft_generator.generate_flights(
            self.mission.country(self.game.blue.country_name),
            self.game.blue.ato,
            tgo_generator.runways,
        )
        aircraft_generator.generate_flights(
            self.mission.country(self.game.red.country_name),
            self.game.red.ato,
            tgo_generator.runways,
        )
        aircraft_generator.spawn_unused_aircraft(
            self.mission.country(self.game.blue.country_name),
            self.mission.country(self.game.red.country_name),
        )

        for flight in aircraft_generator.flights:
            if not flight.client_units:
                continue
            flight.aircraft_type.assign_channels_for_flight(
                flight, air_support_generator.air_support)

        return air_support_generator.air_support, aircraft_generator.flights

    def generate_destroyed_units(self) -> None:
        """Add destroyed units to the Mission"""
        if not self.game.settings.perf_destroyed_units:
            return

        for d in self.game.get_destroyed_units():
            try:
                type_name = d["type"]
                if not isinstance(type_name, str):
                    raise TypeError(
                        "Expected the type of the destroyed static to be a string"
                    )
                utype = unit_type_from_name(type_name)
            except KeyError:
                logging.warning(f"Destroyed unit has no type: {d}")
                continue

            pos = Point(cast(float, d["x"]), cast(float, d["z"]),
                        self.mission.terrain)
            if utype is not None and not self.game.position_culled(pos):
                self.mission.static_group(
                    country=self.mission.country(self.game.blue.country_name),
                    name="",
                    _type=utype,
                    hidden=True,
                    position=pos,
                    heading=d["orientation"],
                    dead=True,
                )

    def describe_air_conflict(self) -> FrontLineConflictDescription:
        player_cp, enemy_cp = self.game.theater.closest_opposing_control_points(
        )
        mid_point = player_cp.position.point_from_heading(
            player_cp.position.heading_between_point(enemy_cp.position),
            player_cp.position.distance_to_point(enemy_cp.position) / 2,
        )
        return FrontLineConflictDescription(
            self.game.theater,
            FrontLine(player_cp, enemy_cp),
            self.game.blue.faction.name,
            self.game.red.faction.name,
            self.mission.country(self.game.blue.country_name),
            self.mission.country(self.game.red.country_name),
            mid_point,
        )

    def notify_info_generators(
        self,
        tgo_generator: TgoGenerator,
        air_support: AirSupport,
        flights: list[FlightData],
    ) -> None:
        """Generates subscribed MissionInfoGenerator objects."""

        gens: list[MissionInfoGenerator] = [
            KneeboardGenerator(self.mission, self.game),
            BriefingGenerator(self.mission, self.game),
        ]
        for gen in gens:
            for dynamic_runway in tgo_generator.runways.values():
                gen.add_dynamic_runway(dynamic_runway)

            for tanker in air_support.tankers:
                if tanker.blue:
                    gen.add_tanker(tanker)

            for aewc in air_support.awacs:
                if aewc.blue:
                    gen.add_awacs(aewc)

            for jtac in air_support.jtacs:
                if jtac.blue:
                    gen.add_jtac(jtac)

            for flight in flights:
                gen.add_flight(flight)
            gen.generate()

    def setup_combined_arms(self) -> None:
        self.mission.groundControl.pilot_can_control_vehicles = COMBINED_ARMS_SLOTS > 0
        self.mission.groundControl.blue_tactical_commander = COMBINED_ARMS_SLOTS
        self.mission.groundControl.blue_observer = 1
Ejemplo n.º 16
0
class MizCampaignLoader:
    BLUE_COUNTRY = CombinedJointTaskForcesBlue()
    RED_COUNTRY = CombinedJointTaskForcesRed()

    OFF_MAP_UNIT_TYPE = F_15C.id

    CV_UNIT_TYPE = CVN_74_John_C__Stennis.id
    LHA_UNIT_TYPE = LHA_1_Tarawa.id
    FRONT_LINE_UNIT_TYPE = Armor.APC_M113.id

    FOB_UNIT_TYPE = Unarmed.CP_SKP_11_ATC_Mobile_Command_Post.id

    EWR_UNIT_TYPE = AirDefence.EWR_55G6.id
    SAM_UNIT_TYPE = AirDefence.SAM_SA_10_S_300PS_SR_64H6E.id
    GARRISON_UNIT_TYPE = AirDefence.SAM_SA_19_Tunguska_2S6.id
    OFFSHORE_STRIKE_TARGET_UNIT_TYPE = Fortification.Oil_platform.id
    SHIP_UNIT_TYPE = USS_Arleigh_Burke_IIa.id
    MISSILE_SITE_UNIT_TYPE = MissilesSS.SRBM_SS_1C_Scud_B_9K72_LN_9P117M.id
    COASTAL_DEFENSE_UNIT_TYPE = MissilesSS.SS_N_2_Silkworm.id

    # Multiple options for the required SAMs so campaign designers can more
    # accurately see the coverage of their IADS for the expected type.
    REQUIRED_LONG_RANGE_SAM_UNIT_TYPES = {
        AirDefence.SAM_Patriot_LN_M901.id,
        AirDefence.SAM_SA_10_S_300PS_LN_5P85C.id,
        AirDefence.SAM_SA_10_S_300PS_LN_5P85D.id,
    }

    REQUIRED_MEDIUM_RANGE_SAM_UNIT_TYPES = {
        AirDefence.SAM_Hawk_LN_M192.id,
        AirDefence.SAM_SA_2_LN_SM_90.id,
        AirDefence.SAM_SA_3_S_125_LN_5P73.id,
    }

    BASE_DEFENSE_RADIUS = nm_to_meter(2)

    def __init__(self, miz: Path, theater: ConflictTheater) -> None:
        self.theater = theater
        self.mission = Mission()
        self.mission.load_file(str(miz))
        self.control_point_id = itertools.count(1000)

        # If there are no red carriers there usually aren't red units. Make sure
        # both countries are initialized so we don't have to deal with None.
        if self.mission.country(self.BLUE_COUNTRY.name) is None:
            self.mission.coalition["blue"].add_country(self.BLUE_COUNTRY)
        if self.mission.country(self.RED_COUNTRY.name) is None:
            self.mission.coalition["red"].add_country(self.RED_COUNTRY)

    @staticmethod
    def control_point_from_airport(airport: Airport) -> ControlPoint:

        # The wiki says this is a legacy property and to just use regular.
        size = SIZE_REGULAR

        # The importance is taken from the periodicity of the airport's
        # warehouse divided by 10. 30 is the default, and out of range (valid
        # values are between 1.0 and 1.4). If it is used, pick the default
        # importance.
        if airport.periodicity == 30:
            importance = IMPORTANCE_MEDIUM
        else:
            importance = airport.periodicity / 10

        cp = Airfield(airport, size, importance)
        cp.captured = airport.is_blue()

        # Use the unlimited aircraft option to determine if an airfield should
        # be owned by the player when the campaign is "inverted".
        cp.captured_invert = airport.unlimited_aircrafts

        return cp

    def country(self, blue: bool) -> Country:
        country = self.mission.country(
            self.BLUE_COUNTRY.name if blue else self.RED_COUNTRY.name)
        # Should be guaranteed because we initialized them.
        assert country
        return country

    @property
    def blue(self) -> Country:
        return self.country(blue=True)

    @property
    def red(self) -> Country:
        return self.country(blue=False)

    def off_map_spawns(self, blue: bool) -> Iterator[FlyingGroup]:
        for group in self.country(blue).plane_group:
            if group.units[0].type == self.OFF_MAP_UNIT_TYPE:
                yield group

    def carriers(self, blue: bool) -> Iterator[ShipGroup]:
        for group in self.country(blue).ship_group:
            if group.units[0].type == self.CV_UNIT_TYPE:
                yield group

    def lhas(self, blue: bool) -> Iterator[ShipGroup]:
        for group in self.country(blue).ship_group:
            if group.units[0].type == self.LHA_UNIT_TYPE:
                yield group

    def fobs(self, blue: bool) -> Iterator[VehicleGroup]:
        for group in self.country(blue).vehicle_group:
            if group.units[0].type == self.FOB_UNIT_TYPE:
                yield group

    @property
    def ships(self) -> Iterator[ShipGroup]:
        for group in self.blue.ship_group:
            if group.units[0].type == self.SHIP_UNIT_TYPE:
                yield group

    @property
    def ewrs(self) -> Iterator[VehicleGroup]:
        for group in self.blue.vehicle_group:
            if group.units[0].type == self.EWR_UNIT_TYPE:
                yield group

    @property
    def sams(self) -> Iterator[VehicleGroup]:
        for group in self.blue.vehicle_group:
            if group.units[0].type == self.SAM_UNIT_TYPE:
                yield group

    @property
    def garrisons(self) -> Iterator[VehicleGroup]:
        for group in self.blue.vehicle_group:
            if group.units[0].type == self.GARRISON_UNIT_TYPE:
                yield group

    @property
    def offshore_strike_targets(self) -> Iterator[StaticGroup]:
        for group in self.blue.static_group:
            if group.units[0].type == self.OFFSHORE_STRIKE_TARGET_UNIT_TYPE:
                yield group

    @property
    def missile_sites(self) -> Iterator[VehicleGroup]:
        for group in self.blue.vehicle_group:
            if group.units[0].type == self.MISSILE_SITE_UNIT_TYPE:
                yield group

    @property
    def coastal_defenses(self) -> Iterator[VehicleGroup]:
        for group in self.blue.vehicle_group:
            if group.units[0].type == self.COASTAL_DEFENSE_UNIT_TYPE:
                yield group

    @property
    def required_long_range_sams(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[0].type in self.REQUIRED_LONG_RANGE_SAM_UNIT_TYPES:
                yield group

    @property
    def required_medium_range_sams(self) -> Iterator[VehicleGroup]:
        for group in self.red.vehicle_group:
            if group.units[
                    0].type in self.REQUIRED_MEDIUM_RANGE_SAM_UNIT_TYPES:
                yield group

    @cached_property
    def control_points(self) -> Dict[int, ControlPoint]:
        control_points = {}
        for airport in self.mission.terrain.airport_list():
            if airport.is_blue() or airport.is_red():
                control_point = self.control_point_from_airport(airport)
                control_points[control_point.id] = control_point

        for blue in (False, True):
            for group in self.off_map_spawns(blue):
                control_point = OffMapSpawn(next(self.control_point_id),
                                            str(group.name), group.position)
                control_point.captured = blue
                control_point.captured_invert = group.late_activation
                control_points[control_point.id] = control_point
            for group in self.carriers(blue):
                # TODO: Name the carrier.
                control_point = Carrier("carrier", group.position,
                                        next(self.control_point_id))
                control_point.captured = blue
                control_point.captured_invert = group.late_activation
                control_points[control_point.id] = control_point
            for group in self.lhas(blue):
                # TODO: Name the LHA.
                control_point = Lha("lha", group.position,
                                    next(self.control_point_id))
                control_point.captured = blue
                control_point.captured_invert = group.late_activation
                control_points[control_point.id] = control_point
            for group in self.fobs(blue):
                control_point = Fob(str(group.name), group.position,
                                    next(self.control_point_id))
                control_point.captured = blue
                control_point.captured_invert = group.late_activation
                control_points[control_point.id] = control_point

        return control_points

    @property
    def front_line_path_groups(self) -> Iterator[VehicleGroup]:
        for group in self.country(blue=True).vehicle_group:
            if group.units[0].type == self.FRONT_LINE_UNIT_TYPE:
                yield group

    @cached_property
    def front_lines(self) -> Dict[str, ComplexFrontLine]:
        # Dict of front line ID to a front line.
        front_lines = {}
        for group in self.front_line_path_groups:
            # The unit will have its first waypoint at the source CP and the
            # final waypoint at the destination CP. Intermediate waypoints
            # define the curve of the front line.
            waypoints = [p.position for p in group.points]
            origin = self.theater.closest_control_point(waypoints[0])
            if origin is None:
                raise RuntimeError(
                    f"No control point near the first waypoint of {group.name}"
                )
            destination = self.theater.closest_control_point(waypoints[-1])
            if destination is None:
                raise RuntimeError(
                    f"No control point near the final waypoint of {group.name}"
                )

            # Snap the begin and end points to the control points.
            waypoints[0] = origin.position
            waypoints[-1] = destination.position
            front_line_id = f"{origin.id}|{destination.id}"
            front_lines[front_line_id] = ComplexFrontLine(origin, waypoints)
            self.control_points[origin.id].connect(
                self.control_points[destination.id])
            self.control_points[destination.id].connect(
                self.control_points[origin.id])
        return front_lines

    def objective_info(self, group: Group) -> Tuple[ControlPoint, int]:
        closest = self.theater.closest_control_point(group.position)
        distance = closest.position.distance_to_point(group.position)
        return closest, distance

    def add_preset_locations(self) -> None:
        for group in self.garrisons:
            closest, distance = self.objective_info(group)
            if distance < self.BASE_DEFENSE_RADIUS:
                closest.preset_locations.base_garrisons.append(group.position)
            else:
                logging.warning(
                    f"Found garrison unit too far from base: {group.name}")

        for group in self.sams:
            closest, distance = self.objective_info(group)
            if distance < self.BASE_DEFENSE_RADIUS:
                closest.preset_locations.base_air_defense.append(
                    group.position)
            else:
                closest.preset_locations.strike_locations.append(
                    group.position)

        for group in self.ewrs:
            closest, distance = self.objective_info(group)
            closest.preset_locations.ewrs.append(group.position)

        for group in self.offshore_strike_targets:
            closest, distance = self.objective_info(group)
            closest.preset_locations.offshore_strike_locations.append(
                group.position)

        for group in self.ships:
            closest, distance = self.objective_info(group)
            closest.preset_locations.ships.append(group.position)

        for group in self.missile_sites:
            closest, distance = self.objective_info(group)
            closest.preset_locations.missile_sites.append(group.position)

        for group in self.coastal_defenses:
            closest, distance = self.objective_info(group)
            closest.preset_locations.coastal_defenses.append(group.position)

        for group in self.required_long_range_sams:
            closest, distance = self.objective_info(group)
            closest.preset_locations.required_long_range_sams.append(
                group.position)

        for group in self.required_medium_range_sams:
            closest, distance = self.objective_info(group)
            closest.preset_locations.required_medium_range_sams.append(
                group.position)

    def populate_theater(self) -> None:
        for control_point in self.control_points.values():
            self.theater.add_controlpoint(control_point)
        self.add_preset_locations()
        self.theater.set_frontline_data(self.front_lines)
Ejemplo n.º 17
0
class Operation:
    attackers_starting_position = None  # type: db.StartingPosition
    defenders_starting_position = None  # type: db.StartingPosition

    current_mission = None  # type: Mission
    regular_mission = None  # type: Mission
    quick_mission = None  # type: Mission
    conflict = None  # type: Conflict
    airgen = None  # type: AircraftConflictGenerator
    triggersgen = None  # type: TriggersGenerator
    airsupportgen = None  # type: AirSupportConflictGenerator
    visualgen = None  # type: VisualGenerator
    groundobjectgen = None  # type: GroundObjectsGenerator
    briefinggen = None  # type: BriefingGenerator
    forcedoptionsgen = None  # type: ForcedOptionsGenerator
    radio_registry: Optional[RadioRegistry] = None
    tacan_registry: Optional[TacanRegistry] = None

    environment_settings = None
    trigger_radius = TRIGGER_RADIUS_MEDIUM
    is_quick = None
    is_awacs_enabled = False
    ca_slots = 0

    def __init__(self, game, attacker_name: str, defender_name: str,
                 from_cp: ControlPoint, departure_cp: ControlPoint,
                 to_cp: ControlPoint):
        self.game = game
        self.attacker_name = attacker_name
        self.attacker_country = db.FACTIONS[attacker_name].country
        self.defender_name = defender_name
        self.defender_country = db.FACTIONS[defender_name].country
        print(self.defender_country, self.attacker_country)
        self.from_cp = from_cp
        self.departure_cp = departure_cp
        self.to_cp = to_cp
        self.is_quick = False
        self.plugin_scripts: List[str] = []

    def units_of(self, country_name: str) -> List[UnitType]:
        return []

    def is_successfull(self, debriefing: Debriefing) -> bool:
        return True

    @property
    def is_player_attack(self) -> bool:
        return self.from_cp.captured

    def initialize(self, mission: Mission, conflict: Conflict):
        self.current_mission = mission
        self.conflict = conflict
        # self.briefinggen = BriefingGenerator(self.current_mission, self.game)  Is it safe to remove this, or does it also break save compat?

    def prepare(self, terrain: Terrain, is_quick: bool):
        with open("resources/default_options.lua", "r") as f:
            options_dict = loads(f.read())["options"]

        self.current_mission = Mission(terrain)

        print(self.game.player_country)
        print(country_dict[db.country_id_from_name(self.game.player_country)])
        print(country_dict[db.country_id_from_name(
            self.game.player_country)]())

        # Setup coalition :
        self.current_mission.coalition["blue"] = Coalition("blue")
        self.current_mission.coalition["red"] = Coalition("red")

        p_country = self.game.player_country
        e_country = self.game.enemy_country
        self.current_mission.coalition["blue"].add_country(
            country_dict[db.country_id_from_name(p_country)]())
        self.current_mission.coalition["red"].add_country(
            country_dict[db.country_id_from_name(e_country)]())

        print([
            c for c in self.current_mission.coalition["blue"].countries.keys()
        ])
        print([
            c for c in self.current_mission.coalition["red"].countries.keys()
        ])

        if is_quick:
            self.quick_mission = self.current_mission
        else:
            self.regular_mission = self.current_mission

        self.current_mission.options.load_from_dict(options_dict)
        self.is_quick = is_quick

        if is_quick:
            self.attackers_starting_position = None
            self.defenders_starting_position = None
        else:
            self.attackers_starting_position = self.departure_cp.at
            # TODO: Is this possible?
            if self.to_cp is not None:
                self.defenders_starting_position = self.to_cp.at
            else:
                self.defenders_starting_position = None

    def inject_lua_trigger(self, contents: str, comment: str) -> None:
        trigger = TriggerStart(comment=comment)
        trigger.add_action(DoScript(String(contents)))
        self.current_mission.triggerrules.triggers.append(trigger)

    def bypass_plugin_script(self, mnemonic: str) -> None:
        self.plugin_scripts.append(mnemonic)

    def inject_plugin_script(self, plugin_mnemonic: str, script: str,
                             script_mnemonic: str) -> None:
        if script_mnemonic in self.plugin_scripts:
            logging.debug(
                f"Skipping already loaded {script} for {plugin_mnemonic}")
        else:
            self.plugin_scripts.append(script_mnemonic)

            plugin_path = Path("./resources/plugins", plugin_mnemonic)

            script_path = Path(plugin_path, script)
            if not script_path.exists():
                logging.error(
                    f"Cannot find {script_path} for plugin {plugin_mnemonic}")
                return

            trigger = TriggerStart(comment=f"Load {script_mnemonic}")
            filename = script_path.resolve()
            fileref = self.current_mission.map_resource.add_resource_file(
                filename)
            trigger.add_action(DoScriptFile(fileref))
            self.current_mission.triggerrules.triggers.append(trigger)

    def notify_info_generators(
        self,
        groundobjectgen: GroundObjectsGenerator,
        airsupportgen: AirSupportConflictGenerator,
        jtacs: List[JtacInfo],
        airgen: AircraftConflictGenerator,
    ):
        """Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)
        """
        gens: List[MissionInfoGenerator] = [
            KneeboardGenerator(self.current_mission, self.game),
            BriefingGenerator(self.current_mission, self.game)
        ]
        for gen in gens:
            for dynamic_runway in groundobjectgen.runways.values():
                gen.add_dynamic_runway(dynamic_runway)

            for tanker in airsupportgen.air_support.tankers:
                gen.add_tanker(tanker)

            if self.is_awacs_enabled:
                for awacs in airsupportgen.air_support.awacs:
                    gen.add_awacs(awacs)

            for jtac in jtacs:
                gen.add_jtac(jtac)

            for flight in airgen.flights:
                gen.add_flight(flight)
            gen.generate()

    def generate(self):
        radio_registry = RadioRegistry()
        tacan_registry = TacanRegistry()

        # Dedup beacon/radio frequencies, since some maps have some frequencies
        # used multiple times.
        beacons = load_beacons_for_terrain(self.game.theater.terrain.name)
        unique_map_frequencies: Set[RadioFrequency] = set()
        for beacon in beacons:
            unique_map_frequencies.add(beacon.frequency)
            if beacon.is_tacan:
                if beacon.channel is None:
                    logging.error(
                        f"TACAN beacon has no channel: {beacon.callsign}")
                else:
                    tacan_registry.reserve(beacon.tacan_channel)

        for airfield, data in AIRFIELD_DATA.items():
            if data.theater == self.game.theater.terrain.name:
                unique_map_frequencies.add(data.atc.hf)
                unique_map_frequencies.add(data.atc.vhf_fm)
                unique_map_frequencies.add(data.atc.vhf_am)
                unique_map_frequencies.add(data.atc.uhf)
                # No need to reserve ILS or TACAN because those are in the
                # beacon list.

        for frequency in unique_map_frequencies:
            radio_registry.reserve(frequency)

        # Set mission time and weather conditions.
        EnvironmentGenerator(self.current_mission,
                             self.game.conditions).generate()

        # Generate ground object first

        groundobjectgen = GroundObjectsGenerator(self.current_mission,
                                                 self.conflict, self.game,
                                                 radio_registry,
                                                 tacan_registry)
        groundobjectgen.generate()

        # Generate destroyed units
        for d in self.game.get_destroyed_units():
            try:
                utype = db.unit_type_from_name(d["type"])
            except KeyError:
                continue

            pos = Point(d["x"], d["z"])
            if utype is not None and not self.game.position_culled(
                    pos) and self.game.settings.perf_destroyed_units:
                self.current_mission.static_group(
                    country=self.current_mission.country(
                        self.game.player_country),
                    name="",
                    _type=utype,
                    hidden=True,
                    position=pos,
                    heading=d["orientation"],
                    dead=True,
                )

        # Air Support (Tanker & Awacs)
        airsupportgen = AirSupportConflictGenerator(self.current_mission,
                                                    self.conflict, self.game,
                                                    radio_registry,
                                                    tacan_registry)
        airsupportgen.generate(self.is_awacs_enabled)

        # Generate Activity on the map
        airgen = AircraftConflictGenerator(self.current_mission, self.conflict,
                                           self.game.settings, self.game,
                                           radio_registry)

        airgen.generate_flights(
            self.current_mission.country(self.game.player_country),
            self.game.blue_ato, groundobjectgen.runways)
        airgen.generate_flights(
            self.current_mission.country(self.game.enemy_country),
            self.game.red_ato, groundobjectgen.runways)

        # Generate ground units on frontline everywhere
        jtacs: List[JtacInfo] = []
        for front_line in self.game.theater.conflicts(True):
            player_cp = front_line.control_point_a
            enemy_cp = front_line.control_point_b
            conflict = Conflict.frontline_cas_conflict(
                self.attacker_name, self.defender_name,
                self.current_mission.country(self.attacker_country),
                self.current_mission.country(self.defender_country), player_cp,
                enemy_cp, self.game.theater)
            # Generate frontline ops
            player_gp = self.game.ground_planners[player_cp.id].units_per_cp[
                enemy_cp.id]
            enemy_gp = self.game.ground_planners[enemy_cp.id].units_per_cp[
                player_cp.id]
            groundConflictGen = GroundConflictGenerator(
                self.current_mission, conflict, self.game, player_gp, enemy_gp,
                player_cp.stances[enemy_cp.id])
            groundConflictGen.generate()
            jtacs.extend(groundConflictGen.jtacs)

        # Setup combined arms parameters
        self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
        if self.game.player_country in [
                country.name for country in
                self.current_mission.coalition["blue"].countries.values()
        ]:
            self.current_mission.groundControl.blue_tactical_commander = self.ca_slots
        else:
            self.current_mission.groundControl.red_tactical_commander = self.ca_slots

        # Triggers
        triggersgen = TriggersGenerator(self.current_mission, self.conflict,
                                        self.game)
        triggersgen.generate()

        # Options
        forcedoptionsgen = ForcedOptionsGenerator(self.current_mission,
                                                  self.conflict, self.game)
        forcedoptionsgen.generate()

        # Generate Visuals Smoke Effects
        visualgen = VisualGenerator(self.current_mission, self.conflict,
                                    self.game)
        if self.game.settings.perf_smoke_gen:
            visualgen.generate()

        luaData = {}
        luaData["AircraftCarriers"] = {}
        luaData["Tankers"] = {}
        luaData["AWACs"] = {}
        luaData["JTACs"] = {}
        luaData["TargetPoints"] = {}

        self.assign_channels_to_flights(airgen.flights,
                                        airsupportgen.air_support)

        for tanker in airsupportgen.air_support.tankers:
            luaData["Tankers"][tanker.callsign] = {
                "dcsGroupName": tanker.dcsGroupName,
                "callsign": tanker.callsign,
                "variant": tanker.variant,
                "radio": tanker.freq.mhz,
                "tacan": str(tanker.tacan.number) + tanker.tacan.band.name
            }

        if self.is_awacs_enabled:
            for awacs in airsupportgen.air_support.awacs:
                luaData["AWACs"][awacs.callsign] = {
                    "dcsGroupName": awacs.dcsGroupName,
                    "callsign": awacs.callsign,
                    "radio": awacs.freq.mhz
                }

        for jtac in jtacs:
            luaData["JTACs"][jtac.callsign] = {
                "dcsGroupName": jtac.dcsGroupName,
                "callsign": jtac.callsign,
                "zone": jtac.region,
                "dcsUnit": jtac.unit_name,
                "laserCode": jtac.code
            }

        for flight in airgen.flights:
            if flight.friendly and flight.flight_type in [
                    FlightType.ANTISHIP, FlightType.DEAD, FlightType.SEAD,
                    FlightType.STRIKE
            ]:
                flightType = flight.flight_type.name
                flightTarget = flight.package.target
                if flightTarget:
                    flightTargetName = None
                    flightTargetType = None
                    if hasattr(flightTarget, 'obj_name'):
                        flightTargetName = flightTarget.obj_name
                        flightTargetType = flightType + f" TGT ({flightTarget.category})"
                    elif hasattr(flightTarget, 'name'):
                        flightTargetName = flightTarget.name
                        flightTargetType = flightType + " TGT (Airbase)"
                    luaData["TargetPoints"][flightTargetName] = {
                        "name": flightTargetName,
                        "type": flightTargetType,
                        "position": {
                            "x": flightTarget.position.x,
                            "y": flightTarget.position.y
                        }
                    }

        # set a LUA table with data from Liberation that we want to set
        # at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function
        # later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts
        state_location = "[[" + os.path.abspath(".") + "]]"
        lua = """
-- setting configuration table
env.info("DCSLiberation|: setting configuration table")

-- all data in this table is overridable.
dcsLiberation = {}

-- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory
dcsLiberation.installPath=""" + state_location + """

"""
        # Process the tankers
        lua += """

-- list the tankers generated by Liberation
dcsLiberation.Tankers = {
"""
        for key in luaData["Tankers"]:
            data = luaData["Tankers"][key]
            dcsGroupName = data["dcsGroupName"]
            callsign = data["callsign"]
            variant = data["variant"]
            tacan = data["tacan"]
            radio = data["radio"]
            lua += f"    {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', variant='{variant}', tacan='{tacan}', radio='{radio}' }}, \n"
            #lua += f"    {{name='{dcsGroupName}', description='{callsign} ({variant})', information='Tacan:{tacan} Radio:{radio}' }}, \n"
        lua += "}"

        # Process the AWACSes
        lua += """

-- list the AWACs generated by Liberation
dcsLiberation.AWACs = {
"""
        for key in luaData["AWACs"]:
            data = luaData["AWACs"][key]
            dcsGroupName = data["dcsGroupName"]
            callsign = data["callsign"]
            radio = data["radio"]
            lua += f"    {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', radio='{radio}' }}, \n"
            #lua += f"    {{name='{dcsGroupName}', description='{callsign} (AWACS)', information='Radio:{radio}' }}, \n"
        lua += "}"

        # Process the JTACs
        lua += """

-- list the JTACs generated by Liberation
dcsLiberation.JTACs = {
"""
        for key in luaData["JTACs"]:
            data = luaData["JTACs"][key]
            dcsGroupName = data["dcsGroupName"]
            callsign = data["callsign"]
            zone = data["zone"]
            laserCode = data["laserCode"]
            dcsUnit = data["dcsUnit"]
            lua += f"    {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', zone='{zone}', laserCode='{laserCode}', dcsUnit='{dcsUnit}' }}, \n"
            #lua += f"    {{name='{dcsGroupName}', description='JTAC {callsign} ', information='Laser:{laserCode}', jtac={laserCode} }}, \n"
        lua += "}"

        # Process the Target Points
        lua += """

-- list the target points generated by Liberation
dcsLiberation.TargetPoints = {
"""
        for key in luaData["TargetPoints"]:
            data = luaData["TargetPoints"][key]
            name = data["name"]
            pointType = data["type"]
            positionX = data["position"]["x"]
            positionY = data["position"]["y"]
            lua += f"    {{name='{name}', pointType='{pointType}', positionX='{positionX}', positionY='{positionY}' }}, \n"
            #lua += f"    {{name='{pointType} {name}', point{{x={positionX}, z={positionY} }} }}, \n"
        lua += "}"

        lua += """

-- list the airbases generated by Liberation
-- dcsLiberation.Airbases = {}

-- list the aircraft carriers generated by Liberation
-- dcsLiberation.Carriers = {}

-- later, we'll add more data to the table

"""

        trigger = TriggerStart(comment="Set DCS Liberation data")
        trigger.add_action(DoScript(String(lua)))
        self.current_mission.triggerrules.triggers.append(trigger)

        # Inject Plugins Lua Scripts and data
        for plugin in LuaPluginManager.plugins():
            if plugin.enabled:
                plugin.inject_scripts(self)
                plugin.inject_configuration(self)

        self.assign_channels_to_flights(airgen.flights,
                                        airsupportgen.air_support)
        self.notify_info_generators(groundobjectgen, airsupportgen, jtacs,
                                    airgen)

    def assign_channels_to_flights(self, flights: List[FlightData],
                                   air_support: AirSupport) -> None:
        """Assigns preset radio channels for client flights."""
        for flight in flights:
            if not flight.client_units:
                continue
            self.assign_channels_to_flight(flight, air_support)

    def assign_channels_to_flight(self, flight: FlightData,
                                  air_support: AirSupport) -> None:
        """Assigns preset radio channels for a client flight."""
        airframe = flight.aircraft_type

        try:
            aircraft_data = AIRCRAFT_DATA[airframe.id]
        except KeyError:
            logging.warning(f"No aircraft data for {airframe.id}")
            return

        if aircraft_data.channel_allocator is not None:
            aircraft_data.channel_allocator.assign_channels_for_flight(
                flight, air_support)