def _load_all(cls) -> None: for file in Path("resources/groups").glob("*.yaml"): if not file.is_file(): raise RuntimeError(f"{file.name} is not a valid ForceGroup") with file.open(encoding="utf-8") as data_file: data = yaml.safe_load(data_file) name = data["name"] group_tasks = [ GroupTask.by_description(n) for n in data.get("tasks") ] if not group_tasks: logging.error(f"ForceGroup {name} has no valid tasking") continue units: list[UnitType[Any]] = [] for unit in data.get("units"): if GroundUnitType.exists(unit): units.append(GroundUnitType.named(unit)) elif ShipUnitType.exists(unit): units.append(ShipUnitType.named(unit)) else: logging.error( f"Unit {unit} of ForceGroup {name} is invalid") if len(units) == 0: logging.error(f"ForceGroup {name} has no valid units") continue statics = [] for static in data.get("statics", []): static_type = static_type_from_name(static) if static_type is None: logging.error(f"Static {static} for {file} is not valid") else: statics.append(static_type) layouts = [LAYOUTS.by_name(n) for n in data.get("layouts")] if not layouts: logging.error(f"ForceGroup {name} has no valid layouts") continue force_group = ForceGroup( name=name, units=units, statics=statics, tasks=group_tasks, layouts=layouts, ) cls._by_name[force_group.name] = force_group cls._loaded = True
def unit_type(self) -> Optional[UnitType[Any]]: if issubclass(self.type, VehicleType): return next(GroundUnitType.for_dcs_type(self.type)) elif issubclass(self.type, ShipType): return next(ShipUnitType.for_dcs_type(self.type)) # None for not available StaticTypes return None
def for_layout(layout: TgoLayout, faction: Faction) -> ForceGroup: """Create a ForceGroup from the given TgoLayout which is usable by the faction This will iterate through all possible TgoLayoutGroups and check if the unit_types are accessible by the faction. All accessible units will be added to the force group """ units: set[UnitType[Any]] = set() statics: set[Type[DcsUnitType]] = set() for group in layout.all_groups: if group.optional and not group.fill: continue for unit_type in group.possible_types_for_faction(faction): if issubclass(unit_type, VehicleType): units.add(next(GroundUnitType.for_dcs_type(unit_type))) elif issubclass(unit_type, ShipType): units.add(next(ShipUnitType.for_dcs_type(unit_type))) elif issubclass(unit_type, StaticType): statics.add(unit_type) return ForceGroup( ", ".join([t.description for t in layout.tasks]), list(units), list(statics), layout.tasks, [layout], )
def unit_types_for_group(self, group: TgoLayoutGroup) -> Iterator[UnitType[Any]]: for dcs_type in self.dcs_unit_types_for_group(group): if issubclass(dcs_type, VehicleType): yield next(GroundUnitType.for_dcs_type(dcs_type)) elif issubclass(dcs_type, ShipType): yield next(ShipUnitType.for_dcs_type(dcs_type))
def update_total_value(self): total_value = 0 if not self.ground_object.purchasable: return for u in self.ground_object.units: # Hack: Unknown variant. unit_type = next(GroundUnitType.for_dcs_type(vehicles.vehicle_map[u.type])) total_value += unit_type.price if self.sell_all_button is not None: self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)") self.total_value = total_value
def make_unit_name(unit: Unit, dead: bool) -> str: dead_label = " [DEAD]" if dead else "" unit_display_name = unit.type dcs_unit_type = vehicle_map.get(unit.type) if dcs_unit_type is not None: # TODO: Make the TGO contain GroundUnitType instead of the pydcs Group. # This is a hack because we can't know which variant was used. try: unit_display_name = next( GroundUnitType.for_dcs_type(dcs_unit_type) ).name except StopIteration: pass return f"Unit #{unit.id} - {unit_display_name}{dead_label}"
def initialize_for_faction(self, faction: Faction) -> ForceGroup: """Initialize a ForceGroup for the given Faction. This adds accessible units to LayoutGroups with the fill property""" for layout in self.layouts: for group in layout.all_groups: if group.fill and not self.has_unit_for_layout_group(group): for unit_type in group.possible_types_for_faction(faction): if issubclass(unit_type, VehicleType): self.units.append( next(GroundUnitType.for_dcs_type(unit_type))) elif issubclass(unit_type, ShipType): self.units.append( next(ShipUnitType.for_dcs_type(unit_type))) elif issubclass(unit_type, StaticType): self.statics.append(unit_type) return self
def add_unit_to_group( self, group: VehicleGroup, unit_type: Type[VehicleType], name: str, position: Point, heading: int, ) -> Vehicle: unit = Vehicle(self.game.next_unit_id(), f"{group.name}|{name}", unit_type.id) unit.position = position unit.heading = heading group.add_unit(unit) # get price of unit to calculate the real price of the whole group try: ground_unit_type = next(GroundUnitType.for_dcs_type(unit_type)) self.price += ground_unit_type.price except StopIteration: logging.error(f"Cannot get price for unit {unit_type.name}") return unit
def doLayout(self): self.update_total_value() self.intelBox = QGroupBox("Units :") self.intelLayout = QGridLayout() i = 0 for g in self.ground_object.groups: if not hasattr(g, "units_losts"): g.units_losts = [] for unit in g.units: unit_display_name = unit.type dcs_unit_type = vehicles.vehicle_map.get(unit.type) if dcs_unit_type is not None: # Hack: Don't know which variant is used. try: unit_display_name = next( GroundUnitType.for_dcs_type(dcs_unit_type) ).name except StopIteration: pass self.intelLayout.addWidget( QLabel( "<b>Unit #" + str(unit.id) + " - " + str(unit_display_name) + "</b>" ), i, 0, ) i = i + 1 for unit in g.units_losts: dcs_unit_type = vehicles.vehicle_map.get(unit.type) if dcs_unit_type is None: continue # Hack: Don't know which variant is used. try: unit_type = next(GroundUnitType.for_dcs_type(dcs_unit_type)) name = unit_type.name price = unit_type.price except StopIteration: name = dcs_unit_type.name price = 0 self.intelLayout.addWidget( QLabel(f"<b>Unit #{unit.id} - {name}</b> [DEAD]"), i, 0 ) if self.cp.captured: repair = QPushButton(f"Repair [{price}M]") repair.setProperty("style", "btn-success") repair.clicked.connect( lambda u=unit, g=g, p=unit_type.price: self.repair_unit(g, u, p) ) self.intelLayout.addWidget(repair, i, 1) i = i + 1 stretch = QVBoxLayout() stretch.addStretch() self.intelLayout.addLayout(stretch, i, 0) self.buildingBox = QGroupBox("Buildings :") self.buildingsLayout = QGridLayout() j = 0 total_income = 0 received_income = 0 for i, building in enumerate(self.buildings): if building.dcs_identifier not in FORTIFICATION_BUILDINGS: self.buildingsLayout.addWidget( QBuildingInfo(building, self.ground_object), j / 3, j % 3 ) j = j + 1 if building.category in REWARDS.keys(): total_income = total_income + REWARDS[building.category] if not building.is_dead: received_income = received_income + REWARDS[building.category] else: logging.warning(building.category + " not in REWARDS") self.financesBox = QGroupBox("Finances: ") self.financesBoxLayout = QGridLayout() self.financesBoxLayout.addWidget( QLabel("Available: " + str(total_income) + "M"), 2, 1 ) self.financesBoxLayout.addWidget( QLabel("Receiving: " + str(received_income) + "M"), 2, 2 ) self.financesBox.setLayout(self.financesBoxLayout) self.buildingBox.setLayout(self.buildingsLayout) self.intelBox.setLayout(self.intelLayout)
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