From 3c2ace09f3ddb8b36902417753e67e76f2f0d878 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Wed, 9 Aug 2023 21:41:51 -0700 Subject: [PATCH] Add display name property for unit types. Unlike the variant ID, this can be changed without breaking save compat. --- changelog.md | 1 + game/dcs/aircrafttype.py | 8 ++++++-- game/dcs/groundunittype.py | 4 +++- game/dcs/shipunittype.py | 4 +++- game/dcs/unittype.py | 3 ++- .../aircraft/flightgroupconfigurator.py | 2 +- game/missiongenerator/airsupportgenerator.py | 2 +- game/purchaseadapter.py | 2 +- qt_ui/windows/AirWingConfigurationDialog.py | 10 +++++----- qt_ui/windows/AirWingDialog.py | 11 ++++++++--- qt_ui/windows/QDebriefingWindow.py | 2 +- qt_ui/windows/QUnitInfoWindow.py | 4 ++-- qt_ui/windows/basemenu/DepartingConvoysMenu.py | 4 ++-- qt_ui/windows/basemenu/NewUnitTransferDialog.py | 6 +++--- .../basemenu/airfield/QAircraftRecruitmentMenu.py | 2 +- .../basemenu/ground_forces/QArmorRecruitmentMenu.py | 2 +- qt_ui/windows/basemenu/intel/QIntelInfo.py | 4 ++-- qt_ui/windows/groundobject/QGroundObjectBuyMenu.py | 2 +- qt_ui/windows/intel.py | 8 ++++---- 19 files changed, 48 insertions(+), 33 deletions(-) diff --git a/changelog.md b/changelog.md index afe31ecf..a92568fe 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ Saves from 8.x are not compatible with 9.0.0. * **[Flight Planning]** Laser codes that are pre-assigned to weapons at mission start can now be chosen from a list in the loadout UI. This does not affect the aircraft's TGP, just the weapons. Currently only implemented for the F-15E S4+ and F-16C. * **[Mission Generation]** Configured target and initial points for F-15E S4+. * **[Modding]** Factions can now specify the ship type to be used for cargo shipping. The Handy Wind will be used by default, but WW2 factions can pick something more appropriate. +* **[Modding]** Unit variants can now set a display name separate from their ID. * **[UI]** An error will be displayed when invalid fast-forward options are selected rather than beginning a never ending simulation. * **[UI]** Added cheats for instantly repairing and destroying runways. * **[UI]** Improved usability of the flight properties UI. It now shows human-readable names and uses more appropriate UI elements. diff --git a/game/dcs/aircrafttype.py b/game/dcs/aircrafttype.py index ff8a8358..e8be18f9 100644 --- a/game/dcs/aircrafttype.py +++ b/game/dcs/aircrafttype.py @@ -291,7 +291,9 @@ class AircraftType(UnitType[Type[FlyingType]]): else: # Slow like warbirds or helicopters # Use whichever is slowest - mach 0.35 or 70% of max speed - logging.debug(f"{self.variant_id} max_speed * 0.7 is {max_speed * 0.7}") + logging.debug( + f"{self.display_name} max_speed * 0.7 is {max_speed * 0.7}" + ) return min(Speed.from_mach(0.35, altitude), max_speed * 0.7) def alloc_flight_radio(self, radio_registry: RadioRegistry) -> RadioFrequency: @@ -471,12 +473,14 @@ class AircraftType(UnitType[Type[FlyingType]]): for task_name, priority in data.get("tasks", {}).items(): task_priorities[FlightType(task_name)] = priority + display_name = data.get("display_name", variant_id) return AircraftType( dcs_unit_type=aircraft, variant_id=variant_id, + display_name=display_name, description=data.get( "description", - f"No data. Google {variant_id}", + f"No data. Google {display_name}", ), year_introduced=introduction, country_of_origin=data.get("origin", "No data."), diff --git a/game/dcs/groundunittype.py b/game/dcs/groundunittype.py index 3f48c00a..9695be48 100644 --- a/game/dcs/groundunittype.py +++ b/game/dcs/groundunittype.py @@ -119,14 +119,16 @@ class GroundUnitType(UnitType[Type[VehicleType]]): else: unit_class = UnitClass(class_name) + display_name = data.get("display_name", variant_id) return GroundUnitType( dcs_unit_type=vehicle, unit_class=unit_class, spawn_weight=data.get("spawn_weight", 0), variant_id=variant_id, + display_name=display_name, description=data.get( "description", - f"No data. Google {variant_id}", + f"No data. Google {display_name}", ), year_introduced=introduction, country_of_origin=data.get("origin", "No data."), diff --git a/game/dcs/shipunittype.py b/game/dcs/shipunittype.py index e6893e52..8610e141 100644 --- a/game/dcs/shipunittype.py +++ b/game/dcs/shipunittype.py @@ -70,13 +70,15 @@ class ShipUnitType(UnitType[Type[ShipType]]): class_name = data.get("class") unit_class = UnitClass(class_name) + display_name = data.get("display_name", variant_id) return ShipUnitType( dcs_unit_type=ship, unit_class=unit_class, variant_id=variant_id, + display_name=data.get("display_name", variant_id), description=data.get( "description", - f"No data. Google {variant_id}", + f"No data. Google {display_name}", ), year_introduced=introduction, country_of_origin=data.get("origin", "No data."), diff --git a/game/dcs/unittype.py b/game/dcs/unittype.py index 54b4700b..29ce0c2d 100644 --- a/game/dcs/unittype.py +++ b/game/dcs/unittype.py @@ -19,6 +19,7 @@ DcsUnitTypeT = TypeVar("DcsUnitTypeT", bound=Type[DcsUnitType]) class UnitType(ABC, Generic[DcsUnitTypeT]): dcs_unit_type: DcsUnitTypeT variant_id: str + display_name: str description: str year_introduced: str country_of_origin: str @@ -30,7 +31,7 @@ class UnitType(ABC, Generic[DcsUnitTypeT]): _loaded: ClassVar[bool] = False def __str__(self) -> str: - return self.variant_id + return self.display_name @property def dcs_id(self) -> str: diff --git a/game/missiongenerator/aircraft/flightgroupconfigurator.py b/game/missiongenerator/aircraft/flightgroupconfigurator.py index 6b7e1ac0..4887e80f 100644 --- a/game/missiongenerator/aircraft/flightgroupconfigurator.py +++ b/game/missiongenerator/aircraft/flightgroupconfigurator.py @@ -164,7 +164,7 @@ class FlightGroupConfigurator: TankerInfo( group_name=str(self.group.name), callsign=callsign, - variant=self.flight.unit_type.variant_id, + variant=self.flight.unit_type.display_name, freq=channel, tacan=tacan, start_time=self.flight.flight_plan.mission_begin_on_station_time, diff --git a/game/missiongenerator/airsupportgenerator.py b/game/missiongenerator/airsupportgenerator.py index d1896919..468196d4 100644 --- a/game/missiongenerator/airsupportgenerator.py +++ b/game/missiongenerator/airsupportgenerator.py @@ -152,7 +152,7 @@ class AirSupportGenerator: TankerInfo( group_name=str(tanker_group.name), callsign=callsign, - variant=tanker_unit_type.variant_id, + variant=tanker_unit_type.display_name, freq=freq, tacan=tacan, start_time=None, diff --git a/game/purchaseadapter.py b/game/purchaseadapter.py index 9e6f99cb..06aa712c 100644 --- a/game/purchaseadapter.py +++ b/game/purchaseadapter.py @@ -140,7 +140,7 @@ class AircraftPurchaseAdapter(PurchaseAdapter[Squadron]): separator = "
" else: separator = " " - return separator.join([item.aircraft.variant_id, str(item)]) + return separator.join([item.aircraft.display_name, str(item)]) def unit_type_of(self, item: Squadron) -> AircraftType: return item.aircraft diff --git a/qt_ui/windows/AirWingConfigurationDialog.py b/qt_ui/windows/AirWingConfigurationDialog.py index f02008cf..c1e464e6 100644 --- a/qt_ui/windows/AirWingConfigurationDialog.py +++ b/qt_ui/windows/AirWingConfigurationDialog.py @@ -594,12 +594,12 @@ class AircraftTypeList(QListView): self.add_aircraft_type(aircraft) def remove_aircraft_type(self, aircraft: AircraftType): - for item in self.item_model.findItems(aircraft.variant_id): + for item in self.item_model.findItems(aircraft.display_name): self.item_model.removeRow(item.row()) self.page_index_changed.emit(self.selectionModel().currentIndex().row()) def add_aircraft_type(self, aircraft: AircraftType): - aircraft_item = QStandardItem(aircraft.variant_id) + aircraft_item = QStandardItem(aircraft.display_name) icon = self.icon_for(aircraft) if icon is not None: aircraft_item.setIcon(icon) @@ -767,7 +767,7 @@ class AirWingConfigurationTab(QWidget): ) # Add Squadron - if not self.type_list.item_model.findItems(selected_type.variant_id): + if not self.type_list.item_model.findItems(selected_type.display_name): self.type_list.add_aircraft_type(selected_type) # TODO Select the newly added type self.squadrons_panel.add_squadron_to_panel(squadron) @@ -893,8 +893,8 @@ class SquadronAircraftTypeSelector(QComboBox): super().__init__() self.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents) - for type in sorted(types, key=lambda type: type.variant_id): - self.addItem(type.variant_id, type) + for type in sorted(types, key=lambda type: type.display_name): + self.addItem(type.display_name, type) if selected_aircraft: self.setCurrentText(selected_aircraft) diff --git a/qt_ui/windows/AirWingDialog.py b/qt_ui/windows/AirWingDialog.py index 9e322ee4..c62f26f7 100644 --- a/qt_ui/windows/AirWingDialog.py +++ b/qt_ui/windows/AirWingDialog.py @@ -44,7 +44,7 @@ class SquadronDelegate(TwoColumnRowDelegate): nickname = "" return f"{squadron.name}{nickname}" elif (row, column) == (0, 1): - return squadron.aircraft.variant_id + return squadron.aircraft.display_name elif (row, column) == (1, 0): return squadron.location.name elif (row, column) == (1, 1): @@ -134,7 +134,7 @@ class AircraftInventoryData: player = "Player" if pilot.player else "AI" yield AircraftInventoryData( flight.departure.name, - flight.unit_type.variant_id, + flight.unit_type.display_name, flight_type, target, pilot_name, @@ -147,7 +147,12 @@ class AircraftInventoryData: ) -> Iterator[AircraftInventoryData]: for _ in range(0, squadron.untasked_aircraft): yield AircraftInventoryData( - squadron.name, squadron.aircraft.variant_id, "Idle", "N/A", "N/A", "N/A" + squadron.name, + squadron.aircraft.display_name, + "Idle", + "N/A", + "N/A", + "N/A", ) diff --git a/qt_ui/windows/QDebriefingWindow.py b/qt_ui/windows/QDebriefingWindow.py index 23bf27e8..5124102a 100644 --- a/qt_ui/windows/QDebriefingWindow.py +++ b/qt_ui/windows/QDebriefingWindow.py @@ -23,7 +23,7 @@ class LossGrid(QGridLayout): super().__init__() self.add_loss_rows( - debriefing.air_losses.by_type(player), lambda u: u.variant_id + debriefing.air_losses.by_type(player), lambda u: u.display_name ) self.add_loss_rows( debriefing.front_line_losses_by_type(player), lambda u: str(u) diff --git a/qt_ui/windows/QUnitInfoWindow.py b/qt_ui/windows/QUnitInfoWindow.py index 375eefa4..b7a4e80a 100644 --- a/qt_ui/windows/QUnitInfoWindow.py +++ b/qt_ui/windows/QUnitInfoWindow.py @@ -51,7 +51,7 @@ class QUnitInfoWindow(QDialog): self.setModal(True) self.game = game self.unit_type = unit_type - self.name = unit_type.variant_id + self.name = unit_type.display_name self.setWindowTitle(f"Unit Info: {self.name}") self.setWindowIcon(QIcon("./resources/icon.png")) self.setMinimumHeight(570) @@ -78,7 +78,7 @@ class QUnitInfoWindow(QDialog): self.details_grid_layout.setContentsMargins(0, 0, 0, 0) self.name_box = QLabel( - f"Name: {unit_type.manufacturer} {unit_type.variant_id}" + f"Name: {unit_type.manufacturer} {unit_type.display_name}" ) self.name_box.setProperty("style", "info-element") diff --git a/qt_ui/windows/basemenu/DepartingConvoysMenu.py b/qt_ui/windows/basemenu/DepartingConvoysMenu.py index 34e25bb9..f6963ce8 100644 --- a/qt_ui/windows/basemenu/DepartingConvoysMenu.py +++ b/qt_ui/windows/basemenu/DepartingConvoysMenu.py @@ -33,11 +33,11 @@ class DepartingConvoyInfo(QGroupBox): if unit_type.dcs_id in VEHICLES_ICONS.keys(): icon.setPixmap(VEHICLES_ICONS[unit_type.dcs_id]) else: - icon.setText("" + unit_type.variant_id + "") + icon.setText("" + unit_type.display_name + "") icon.setProperty("style", "icon-armor") unit_layout.addWidget(icon, idx, 0) unit_layout.addWidget( - QLabel(f"{count} x {unit_type.variant_id}"), + QLabel(f"{count} x {unit_type.display_name}"), idx, 1, ) diff --git a/qt_ui/windows/basemenu/NewUnitTransferDialog.py b/qt_ui/windows/basemenu/NewUnitTransferDialog.py index 83dcd590..b93f2f4c 100644 --- a/qt_ui/windows/basemenu/NewUnitTransferDialog.py +++ b/qt_ui/windows/basemenu/NewUnitTransferDialog.py @@ -62,7 +62,7 @@ class UnitTransferList(QFrame): task_box_layout = QGridLayout() scroll_content.setLayout(task_box_layout) - units_column = sorted(cp.base.armor, key=lambda u: u.variant_id) + units_column = sorted(cp.base.armor, key=lambda u: u.display_name) count = 0 for count, unit_type in enumerate(units_column): @@ -171,7 +171,7 @@ class ScrollingUnitTransferGrid(QFrame): unit_types = set(self.game_model.game.faction_for(player=True).ground_units) sorted_units = sorted( {u for u in unit_types if self.cp.base.total_units_of_type(u)}, - key=lambda u: u.variant_id, + key=lambda u: u.display_name, ) for row, unit_type in enumerate(sorted_units): self.add_unit_row(unit_type, task_box_layout, row) @@ -203,7 +203,7 @@ class ScrollingUnitTransferGrid(QFrame): origin_inventory = self.cp.base.total_units_of_type(unit_type) - unit_name = QLabel(f"{unit_type.variant_id}") + unit_name = QLabel(f"{unit_type.display_name}") unit_name.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) ) diff --git a/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py b/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py index 4dbf6bf0..64b1f89d 100644 --- a/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py +++ b/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py @@ -45,7 +45,7 @@ class QAircraftRecruitmentMenu(UnitTransactionFrame[Squadron]): unit_types.add(squadron.aircraft) sorted_squadrons = sorted( - cp.squadrons, key=lambda s: (s.aircraft.variant_id, s.name) + cp.squadrons, key=lambda s: (s.aircraft.display_name, s.name) ) for row, squadron in enumerate(sorted_squadrons): self.add_purchase_row(squadron, task_box_layout, row) diff --git a/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py b/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py index f7481b51..07ef03d9 100644 --- a/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py +++ b/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py @@ -32,7 +32,7 @@ class QArmorRecruitmentMenu(UnitTransactionFrame[GroundUnitType]): unit_types = list( set(self.game_model.game.faction_for(player=True).ground_units) ) - unit_types.sort(key=lambda u: u.variant_id) + unit_types.sort(key=lambda u: u.display_name) for row, unit_type in enumerate(unit_types): self.add_purchase_row(unit_type, task_box_layout, row) stretch = QVBoxLayout() diff --git a/qt_ui/windows/basemenu/intel/QIntelInfo.py b/qt_ui/windows/basemenu/intel/QIntelInfo.py index 3effd35c..c2a0408b 100644 --- a/qt_ui/windows/basemenu/intel/QIntelInfo.py +++ b/qt_ui/windows/basemenu/intel/QIntelInfo.py @@ -27,7 +27,7 @@ class QIntelInfo(QFrame): for unit_type, count in self.cp.allocated_aircraft().present.items(): if count: task_type = unit_type.dcs_unit_type.task_default.name - units_by_task[task_type][unit_type.variant_id] += count + units_by_task[task_type][unit_type.display_name] += count units_by_task = { task: units_by_task[task] for task in sorted(units_by_task.keys()) @@ -36,7 +36,7 @@ class QIntelInfo(QFrame): front_line_units = defaultdict(int) for unit_type, count in self.cp.base.armor.items(): if count: - front_line_units[unit_type.variant_id] += count + front_line_units[unit_type.display_name] += count units_by_task["Front line units"] = front_line_units for task, unit_types in units_by_task.items(): diff --git a/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py b/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py index d5760fb3..647ac060 100644 --- a/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py +++ b/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py @@ -76,7 +76,7 @@ class QTgoLayoutGroupRow(QWidget): # Add all possible units with the price for unit_type in force_group.unit_types_for_group(group): self.unit_selector.addItem( - f"{unit_type.variant_id} [${unit_type.price}M]", + f"{unit_type.display_name} [${unit_type.price}M]", userData=(unit_type.dcs_unit_type, unit_type.price), ) # Add all possible statics with price = 0 diff --git a/qt_ui/windows/intel.py b/qt_ui/windows/intel.py index dffca9b4..0a410704 100644 --- a/qt_ui/windows/intel.py +++ b/qt_ui/windows/intel.py @@ -84,11 +84,11 @@ class AircraftIntelLayout(IntelTableLayout): continue self.add_header(f"{control_point.name} ({base_total})") - for airframe in sorted(allocation.present, key=lambda k: k.variant_id): + for airframe in sorted(allocation.present, key=lambda k: k.display_name): count = allocation.present[airframe] if not count: continue - self.add_row(f" {airframe.variant_id}", count) + self.add_row(f" {airframe.display_name}", count) self.add_row("") self.add_row("Total", total) @@ -113,11 +113,11 @@ class ArmyIntelLayout(IntelTableLayout): continue self.add_header(f"{control_point.name} ({base.total_armor})") - for vehicle in sorted(base.armor, key=lambda k: k.variant_id): + for vehicle in sorted(base.armor, key=lambda k: k.display_name): count = base.armor[vehicle] if not count: continue - self.add_row(f" {vehicle.variant_id}", count) + self.add_row(f" {vehicle.display_name}", count) self.add_row("") self.add_row("Total", total)