def main(): logging_config.init_logging(VERSION) logging.debug("Python version %s", sys.version) if not str(Path(__file__)).isascii(): logging.warning( "Installation path contains non-ASCII characters. This is known to cause problems." ) game: Optional[Game] = None args = parse_args() # TODO: Flesh out data and then make unconditional. if args.warn_missing_weapon_data: lint_all_weapon_data() load_mods() if args.subcommand == "new-game": with logged_duration("New game creation"): game = create_game( args.campaign, args.blue, args.red, args.supercarrier, args.auto_procurement, args.inverted, args.cheats, args.date, args.restrict_weapons_by_date, ) if args.subcommand == "lint-weapons": lint_weapon_data_for_aircraft(AircraftType.named(args.aircraft)) return with Server().run_in_thread(): run_ui(game, args.dev)
def from_yaml(cls, path: Path) -> SquadronDef: from game.ato.ai_flight_planner_db import tasks_for_aircraft from game.ato import FlightType with path.open(encoding="utf8") as squadron_file: data = yaml.safe_load(squadron_file) name = data["aircraft"] try: unit_type = AircraftType.named(name) except KeyError as ex: raise KeyError(f"Could not find any aircraft named {name}") from ex pilots = [Pilot(n, player=False) for n in data.get("pilots", [])] pilots.extend([Pilot(n, player=True) for n in data.get("players", [])]) female_pilot_percentage = data.get("female_pilot_percentage", 6) mission_types = [FlightType.from_name(n) for n in data["mission_types"]] tasks = tasks_for_aircraft(unit_type) for mission_type in list(mission_types): if mission_type not in tasks: logging.error( f"Squadron has mission type {mission_type} but {unit_type} is not " f"capable of that task: {path}" ) mission_types.remove(mission_type) return SquadronDef( name=data["name"], nickname=data.get("nickname"), country=data["country"], role=data["role"], aircraft=unit_type, livery=data.get("livery"), mission_types=tuple(mission_types), operating_bases=OperatingBases.from_yaml(unit_type, data.get("bases", {})), female_pilot_percentage=female_pilot_percentage, pilot_pool=pilots, )
def aircraft_for_task(task: FlightType) -> list[AircraftType]: dcs_types = dcs_types_for_task(task) types: list[AircraftType] = [] for dcs_type in dcs_types: types.extend(AircraftType.for_dcs_type(dcs_type)) return types
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction: faction = Faction(locales=json.get("locales")) faction.country = json.get("country", "/") if faction.country not in [c.name for c in country_dict.values()]: raise AssertionError( 'Faction\'s country ("{}") is not a valid DCS country ID'. format(faction.country)) faction.name = json.get("name", "") if not faction.name: raise AssertionError("Faction has no valid name") faction.authors = json.get("authors", "") faction.description = json.get("description", "") faction.aircrafts = [ AircraftType.named(n) for n in json.get("aircrafts", []) ] faction.awacs = [AircraftType.named(n) for n in json.get("awacs", [])] faction.tankers = [ AircraftType.named(n) for n in json.get("tankers", []) ] faction.aircrafts = list( set(faction.aircrafts + faction.awacs + faction.tankers)) faction.frontline_units = [ GroundUnitType.named(n) for n in json.get("frontline_units", []) ] faction.artillery_units = [ GroundUnitType.named(n) for n in json.get("artillery_units", []) ] faction.infantry_units = [ GroundUnitType.named(n) for n in json.get("infantry_units", []) ] faction.logistics_units = [ GroundUnitType.named(n) for n in json.get("logistics_units", []) ] faction.air_defense_units = [ GroundUnitType.named(n) for n in json.get("air_defense_units", []) ] faction.missiles = [ GroundUnitType.named(n) for n in json.get("missiles", []) ] faction.naval_units = [ ShipUnitType.named(n) for n in json.get("naval_units", []) ] faction.preset_groups = [ ForceGroup.named(n) for n in json.get("preset_groups", []) ] faction.requirements = json.get("requirements", {}) faction.carrier_names = json.get("carrier_names", []) faction.helicopter_carrier_names = json.get("helicopter_carrier_names", []) faction.has_jtac = json.get("has_jtac", False) jtac_name = json.get("jtac_unit", None) if jtac_name is not None: faction.jtac_unit = AircraftType.named(jtac_name) else: faction.jtac_unit = None # Load doctrine doctrine = json.get("doctrine", "modern") if doctrine == "modern": faction.doctrine = MODERN_DOCTRINE elif doctrine == "coldwar": faction.doctrine = COLDWAR_DOCTRINE elif doctrine == "ww2": faction.doctrine = WWII_DOCTRINE else: faction.doctrine = MODERN_DOCTRINE # Load the building set building_set = json.get("building_set", "default") if building_set == "default": faction.building_set = DEFAULT_AVAILABLE_BUILDINGS elif building_set == "ww2free": faction.building_set = WW2_FREE elif building_set == "ww2ally": faction.building_set = WW2_ALLIES_BUILDINGS elif building_set == "ww2germany": faction.building_set = WW2_GERMANY_BUILDINGS else: faction.building_set = DEFAULT_AVAILABLE_BUILDINGS # Add required buildings for the game logic (e.g. ammo, factory..) faction.building_set.extend(REQUIRED_BUILDINGS) # Load liveries override faction.liveries_overrides = {} liveries_overrides = json.get("liveries_overrides", {}) for name, livery in liveries_overrides.items(): aircraft = AircraftType.named(name) faction.liveries_overrides[aircraft] = [s.lower() for s in livery] faction.unrestricted_satnav = json.get("unrestricted_satnav", False) return faction
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction: faction = Faction(locales=json.get("locales")) faction.country = json.get("country", "/") if faction.country not in [c.name for c in country_dict.values()]: raise AssertionError( 'Faction\'s country ("{}") is not a valid DCS country ID'.format( faction.country ) ) faction.name = json.get("name", "") if not faction.name: raise AssertionError("Faction has no valid name") faction.authors = json.get("authors", "") faction.description = json.get("description", "") faction.aircrafts = [AircraftType.named(n) for n in json.get("aircrafts", [])] faction.awacs = [AircraftType.named(n) for n in json.get("awacs", [])] faction.tankers = [AircraftType.named(n) for n in json.get("tankers", [])] faction.aircrafts = list( set(faction.aircrafts + faction.awacs + faction.tankers) ) faction.frontline_units = [ GroundUnitType.named(n) for n in json.get("frontline_units", []) ] faction.artillery_units = [ GroundUnitType.named(n) for n in json.get("artillery_units", []) ] faction.infantry_units = [ GroundUnitType.named(n) for n in json.get("infantry_units", []) ] faction.logistics_units = [ GroundUnitType.named(n) for n in json.get("logistics_units", []) ] faction.ewrs = json.get("ewrs", []) faction.air_defenses = json.get("air_defenses", []) # Compatibility for older factions. All air defenses now belong to a # single group and the generator decides what belongs where. faction.air_defenses.extend(json.get("sams", [])) faction.air_defenses.extend(json.get("shorads", [])) faction.missiles = json.get("missiles", []) faction.coastal_defenses = json.get("coastal_defenses", []) faction.requirements = json.get("requirements", {}) faction.carrier_names = json.get("carrier_names", []) faction.helicopter_carrier_names = json.get("helicopter_carrier_names", []) faction.navy_generators = json.get("navy_generators", []) faction.aircraft_carrier = load_all_ships(json.get("aircraft_carrier", [])) faction.helicopter_carrier = load_all_ships(json.get("helicopter_carrier", [])) faction.destroyers = load_all_ships(json.get("destroyers", [])) faction.cruisers = load_all_ships(json.get("cruisers", [])) faction.has_jtac = json.get("has_jtac", False) jtac_name = json.get("jtac_unit", None) if jtac_name is not None: faction.jtac_unit = AircraftType.named(jtac_name) else: faction.jtac_unit = None faction.navy_group_count = int(json.get("navy_group_count", 1)) faction.missiles_group_count = int(json.get("missiles_group_count", 0)) faction.coastal_group_count = int(json.get("coastal_group_count", 0)) # Load doctrine doctrine = json.get("doctrine", "modern") if doctrine == "modern": faction.doctrine = MODERN_DOCTRINE elif doctrine == "coldwar": faction.doctrine = COLDWAR_DOCTRINE elif doctrine == "ww2": faction.doctrine = WWII_DOCTRINE else: faction.doctrine = MODERN_DOCTRINE # Load the building set building_set = json.get("building_set", "default") if building_set == "default": faction.building_set = DEFAULT_AVAILABLE_BUILDINGS elif building_set == "ww2free": faction.building_set = WW2_FREE elif building_set == "ww2ally": faction.building_set = WW2_ALLIES_BUILDINGS elif building_set == "ww2germany": faction.building_set = WW2_GERMANY_BUILDINGS else: faction.building_set = DEFAULT_AVAILABLE_BUILDINGS # Load liveries override faction.liveries_overrides = {} liveries_overrides = json.get("liveries_overrides", {}) for name, livery in liveries_overrides.items(): aircraft = AircraftType.named(name) faction.liveries_overrides[aircraft] = [s.lower() for s in livery] faction.unrestricted_satnav = json.get("unrestricted_satnav", False) return faction
def generate(self) -> None: position = Conflict.frontline_position( self.conflict.front_line, self.game.theater ) frontline_vector = Conflict.frontline_vector( self.conflict.front_line, self.game.theater ) # Create player groups at random position player_groups = self._generate_groups( self.player_planned_combat_groups, frontline_vector, True ) # Create enemy groups at random position enemy_groups = self._generate_groups( self.enemy_planned_combat_groups, frontline_vector, False ) # TODO: Differentiate AirConflict and GroundConflict classes. if self.conflict.heading is None: raise RuntimeError( "Cannot generate ground units for non-ground conflict. Ground unit " "conflicts cannot have the heading `None`." ) # Plan combat actions for groups self.plan_action_for_groups( self.player_stance, player_groups, enemy_groups, self.conflict.heading + 90, self.conflict.blue_cp, self.conflict.red_cp, ) self.plan_action_for_groups( self.enemy_stance, enemy_groups, player_groups, self.conflict.heading - 90, self.conflict.red_cp, self.conflict.blue_cp, ) # Add JTAC if self.game.player_faction.has_jtac: n = "JTAC" + str(self.conflict.blue_cp.id) + str(self.conflict.red_cp.id) code = 1688 - len(self.jtacs) utype = self.game.player_faction.jtac_unit if utype is None: utype = AircraftType.named("MQ-9 Reaper") jtac = self.mission.flight_group( country=self.mission.country(self.game.player_country), name=n, aircraft_type=utype.dcs_unit_type, position=position[0], airport=None, altitude=5000, ) jtac.points[0].tasks.append(SetInvisibleCommand(True)) jtac.points[0].tasks.append(SetImmortalCommand(True)) jtac.points[0].tasks.append( OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle) ) frontline = ( f"Frontline {self.conflict.blue_cp.name}/{self.conflict.red_cp.name}" ) # Note: Will need to change if we ever add ground based JTAC. callsign = callsign_for_support_unit(jtac) self.jtacs.append( JtacInfo( str(jtac.name), n, callsign, frontline, str(code), blue=True, ) )
def generate(self) -> None: position = FrontLineConflictDescription.frontline_position( self.conflict.front_line, self.game.theater) frontline_vector = FrontLineConflictDescription.frontline_vector( self.conflict.front_line, self.game.theater) # Create player groups at random position player_groups = self._generate_groups( self.player_planned_combat_groups, frontline_vector, True) # Create enemy groups at random position enemy_groups = self._generate_groups(self.enemy_planned_combat_groups, frontline_vector, False) # TODO: Differentiate AirConflict and GroundConflict classes. if self.conflict.heading is None: raise RuntimeError( "Cannot generate ground units for non-ground conflict. Ground unit " "conflicts cannot have the heading `None`.") # Plan combat actions for groups self.plan_action_for_groups( self.player_stance, player_groups, enemy_groups, self.conflict.heading.right, self.conflict.blue_cp, self.conflict.red_cp, ) self.plan_action_for_groups( self.enemy_stance, enemy_groups, player_groups, self.conflict.heading.left, self.conflict.red_cp, self.conflict.blue_cp, ) # Add JTAC if self.game.blue.faction.has_jtac: code: int freq = self.radio_registry.alloc_uhf() # If the option fc3LaserCode is enabled, force all JTAC # laser codes to 1113 to allow lasing for Su-25 Frogfoots and A-10A Warthogs. # Otherwise use 1688 for the first JTAC, 1687 for the second etc. if self.game.settings.plugins["plugins.jtacautolase.fc3LaserCode"]: code = 1113 else: code = self.laser_code_registry.get_next_laser_code() utype = self.game.blue.faction.jtac_unit if utype is None: utype = AircraftType.named("MQ-9 Reaper") jtac = self.mission.flight_group( country=self.mission.country(self.game.blue.country_name), name=namegen.next_jtac_name(), aircraft_type=utype.dcs_unit_type, position=position[0], airport=None, altitude=5000, maintask=AFAC, ) jtac.points[0].tasks.append( FAC(callsign=len(self.air_support.jtacs) + 1, frequency=int(freq.mhz))) jtac.points[0].tasks.append(SetInvisibleCommand(True)) jtac.points[0].tasks.append(SetImmortalCommand(True)) jtac.points[0].tasks.append( OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle)) frontline = ( f"Frontline {self.conflict.blue_cp.name}/{self.conflict.red_cp.name}" ) # Note: Will need to change if we ever add ground based JTAC. callsign = callsign_for_support_unit(jtac) self.air_support.jtacs.append( JtacInfo( jtac.name, jtac.name, callsign, frontline, str(code), blue=True, freq=freq, ))