Ejemplo n.º 1
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.º 2
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.º 3
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