diff --git a/changelog.md b/changelog.md
index df3e696a..414f4401 100644
--- a/changelog.md
+++ b/changelog.md
@@ -216,6 +216,7 @@ BAI/ANTISHIP/DEAD/STRIKE/BARCAP/CAS/OCA/AIR-ASSAULT (main) missions
* **[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 3ef80ac4..cf9ae16b 100644
--- a/game/dcs/aircrafttype.py
+++ b/game/dcs/aircrafttype.py
@@ -297,7 +297,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
else:
# Slow like warbirds or helicopters
# Use whichever is slowest - mach 0.35 or 50% of max speed
- logging.debug(f"{self.variant_id} max_speed * 0.5 is {max_speed * 0.5}")
+ logging.debug(f"{self.display_name} max_speed * 0.5 is {max_speed * 0.5}")
return min(Speed.from_mach(0.35, altitude), max_speed * 0.5)
def alloc_flight_radio(self, radio_registry: RadioRegistry) -> RadioFrequency:
@@ -477,12 +477,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 8387165e..3f795544 100644
--- a/game/dcs/groundunittype.py
+++ b/game/dcs/groundunittype.py
@@ -117,14 +117,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 bd81948f..6134f290 100644
--- a/game/dcs/shipunittype.py
+++ b/game/dcs/shipunittype.py
@@ -68,13 +68,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 4e4224c0..e8925bba 100644
--- a/game/dcs/unittype.py
+++ b/game/dcs/unittype.py
@@ -20,6 +20,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
@@ -31,7 +32,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 e39cc701..effbd010 100644
--- a/game/missiongenerator/aircraft/flightgroupconfigurator.py
+++ b/game/missiongenerator/aircraft/flightgroupconfigurator.py
@@ -209,7 +209,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.patrol_start_time,
diff --git a/game/purchaseadapter.py b/game/purchaseadapter.py
index 5f2a801e..5b1968bc 100644
--- a/game/purchaseadapter.py
+++ b/game/purchaseadapter.py
@@ -142,7 +142,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 68367f39..3afa106a 100644
--- a/qt_ui/windows/AirWingConfigurationDialog.py
+++ b/qt_ui/windows/AirWingConfigurationDialog.py
@@ -611,12 +611,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)
@@ -728,7 +728,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)
@@ -814,8 +814,8 @@ class SquadronAircraftTypeSelector(QComboBox):
super().__init__()
self.setSizeAdjustPolicy(self.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 42477acc..bb2725a3 100644
--- a/qt_ui/windows/AirWingDialog.py
+++ b/qt_ui/windows/AirWingDialog.py
@@ -43,7 +43,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):
@@ -130,7 +130,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,
@@ -143,7 +143,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 e4aefc64..1c51c083 100644
--- a/qt_ui/windows/QDebriefingWindow.py
+++ b/qt_ui/windows/QDebriefingWindow.py
@@ -24,7 +24,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 904cb146..412679d7 100644
--- a/qt_ui/windows/QUnitInfoWindow.py
+++ b/qt_ui/windows/QUnitInfoWindow.py
@@ -66,7 +66,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)
@@ -93,7 +93,7 @@ class QUnitInfoWindow(QDialog):
self.details_grid_layout.setMargin(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 c1da87e3..b2ee9c5b 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 b7a2c299..43578cb1 100644
--- a/qt_ui/windows/basemenu/NewUnitTransferDialog.py
+++ b/qt_ui/windows/basemenu/NewUnitTransferDialog.py
@@ -64,7 +64,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):
@@ -173,7 +173,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)
@@ -205,7 +205,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 95d487d4..eb8c8219 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 e420db3b..2dd74174 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 fe9f9c85..ed639e5e 100644
--- a/qt_ui/windows/basemenu/intel/QIntelInfo.py
+++ b/qt_ui/windows/basemenu/intel/QIntelInfo.py
@@ -32,7 +32,7 @@ class QIntelInfo(QFrame):
).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())
@@ -41,7 +41,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 c790287b..db125074 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 d8742273..fd6bde8a 100644
--- a/qt_ui/windows/intel.py
+++ b/qt_ui/windows/intel.py
@@ -88,11 +88,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)
@@ -117,11 +117,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)