diff --git a/game/operation/operation.py b/game/operation/operation.py index ec2a086a..c24d627a 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -26,12 +26,12 @@ from gen.environmentgen import EnvironmentGenerator from gen.forcedoptionsgen import ForcedOptionsGenerator from gen.groundobjectsgen import GroundObjectsGenerator from gen.kneeboard import KneeboardGenerator +from gen.naming import namegen from gen.radios import RadioFrequency, RadioRegistry from gen.tacan import TacanRegistry from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator from .. import db -from ..debriefing import Debriefing from ..theater import Airfield from ..unitmap import UnitMap @@ -86,7 +86,7 @@ class Operation: cls.game.enemy_country, frontline.position ) - + @classmethod def air_conflict(cls) -> Conflict: assert cls.game @@ -103,7 +103,7 @@ class Operation: cls.game.enemy_name, cls.game.player_country, cls.game.enemy_country, - mid_point + mid_point ) @classmethod @@ -295,7 +295,7 @@ class Operation: heading=d["orientation"], dead=True, ) - + @classmethod def generate(cls) -> UnitMap: """Build the final Mission to be exported""" @@ -349,7 +349,7 @@ class Operation: cls.jtacs, cls.airgen ) - + cls.reset_naming_ids() return cls.unit_map @classmethod @@ -411,6 +411,15 @@ class Operation: ground_conflict_gen.generate() cls.jtacs.extend(ground_conflict_gen.jtacs) + @classmethod + def reset_naming_ids(cls): + if not cls.game: + logging.warning("Game object not initialized before resetting IDs") + return + cls.game.current_group_id = 0 + cls.game.current_unit_id = 0 + namegen.reset_numbers() + @classmethod def generate_lua(cls, airgen: AircraftConflictGenerator, airsupportgen: AirSupportConflictGenerator, diff --git a/gen/aircraft.py b/gen/aircraft.py index 788b969d..3c8065bf 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -971,8 +971,8 @@ class AircraftConflictGenerator: arrival=control_point, divert=None) group = self._generate_at_airport( - name=namegen.next_unit_name(country, control_point.id, - aircraft), + name=namegen.next_aircraft_name(country, control_point.id, + flight), side=country, unit_type=aircraft, count=1, @@ -1036,17 +1036,18 @@ class AircraftConflictGenerator: CoalitionHasAirdrome(coalition, flight.from_cp.id)) def generate_planned_flight(self, cp, country, flight:Flight): + name = namegen.next_aircraft_name(country, cp.id, flight) try: if flight.start_type == "In Flight": group = self._generate_inflight( - name=namegen.next_unit_name(country, cp.id, flight.unit_type), + name=name, side=country, flight=flight, origin=cp) elif isinstance(cp, NavalControlPoint): group_name = cp.get_carrier_group_name() group = self._generate_at_group( - name=namegen.next_unit_name(country, cp.id, flight.unit_type), + name=name, side=country, unit_type=flight.unit_type, count=flight.count, @@ -1057,8 +1058,7 @@ class AircraftConflictGenerator: raise RuntimeError( f"Attempted to spawn at airfield for non-airfield {cp}") group = self._generate_at_airport( - name=namegen.next_unit_name(country, cp.id, - flight.unit_type), + name=name, side=country, unit_type=flight.unit_type, count=flight.count, @@ -1070,7 +1070,7 @@ class AircraftConflictGenerator: logging.warning("No room on runway or parking slots. Starting from the air.") flight.start_type = "In Flight" group = self._generate_inflight( - name=namegen.next_unit_name(country, cp.id, flight.unit_type), + name=name, side=country, flight=flight, origin=cp) diff --git a/gen/armor.py b/gen/armor.py index e6b73e54..9cc0c090 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -206,7 +206,7 @@ class GroundConflictGenerator: u = random.choice(manpads) self.mission.vehicle_group( side, - namegen.next_infantry_name(side, cp, u), u, + namegen.next_infantry_name(side, cp.id, u), u, position=infantry_position, group_size=1, heading=forward_heading, @@ -220,7 +220,7 @@ class GroundConflictGenerator: u = random.choice(possible_infantry_units) self.mission.vehicle_group( side, - namegen.next_infantry_name(side, cp, u), u, + namegen.next_infantry_name(side, cp.id, u), u, position=infantry_position, group_size=1, heading=forward_heading, @@ -231,7 +231,7 @@ class GroundConflictGenerator: position = infantry_position.random_point_within(55, 5) self.mission.vehicle_group( side, - namegen.next_infantry_name(side, cp, u), u, + namegen.next_infantry_name(side, cp.id, u), u, position=position, group_size=1, heading=forward_heading, diff --git a/gen/flights/flight.py b/gen/flights/flight.py index 9b659c5f..0b784025 100644 --- a/gen/flights/flight.py +++ b/gen/flights/flight.py @@ -137,7 +137,8 @@ class Flight: def __init__(self, package: Package, unit_type: Type[FlyingType], count: int, flight_type: FlightType, start_type: str, departure: ControlPoint, arrival: ControlPoint, - divert: Optional[ControlPoint]) -> None: + divert: Optional[ControlPoint], + custom_name: Optional[str] = None) -> None: self.package = package self.unit_type = unit_type self.count = count @@ -151,6 +152,7 @@ class Flight: self.start_type = start_type self.use_custom_loadout = False self.client_count = 0 + self.custom_name = custom_name # Will be replaced with a more appropriate FlightPlan by # FlightPlanBuilder, but an empty flight plan the flight begins with an @@ -172,4 +174,6 @@ class Flight: def __repr__(self): name = db.unit_type_name(self.unit_type) + if self.custom_name: + return f"{self.custom_name} {self.count} x {name}" return f"[{self.flight_type}] {self.count} x {name}" diff --git a/gen/naming.py b/gen/naming.py index 40da3a6b..9989ef69 100644 --- a/gen/naming.py +++ b/gen/naming.py @@ -1,16 +1,19 @@ -from game import db import random -ALPHA_MILITARY = ["Alpha","Bravo","Charlie","Delta","Echo","Foxtrot", - "Golf","Hotel","India","Juliet","Kilo","Lima","Mike", - "November","Oscar","Papa","Quebec","Romeo","Sierra", - "Tango","Uniform","Victor","Whisky","XRay","Yankee", - "Zulu","Zero"] +from dcs.country import Country +from dcs.unittype import UnitType -class NameGenerator: - number = 0 +from game import db - ANIMALS = [ +from gen.flights.flight import Flight + +ALPHA_MILITARY = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", + "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", + "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", + "Tango", "Uniform", "Victor", "Whisky", "XRay", "Yankee", + "Zulu", "Zero"] + +ANIMALS = [ "SHARK", "TORTOISE", "BAT", "PANGOLIN", "AARDWOLF", "MONKEY", "BUFFALO", "DOG", "BOBCAT", "LYNX", "PANTHER", "TIGER", "LION", "OWL", "BUTTERFLY", "BISON", "DUCK", "COBRA", "MAMBA", @@ -38,47 +41,76 @@ class NameGenerator: "ANACONDA" ] - def __init__(self): - self.number = 0 - self.ANIMALS = NameGenerator.ANIMALS.copy() +class NameGenerator: + number = 0 + infantry_number = 0 + aircraft_number = 0 - def reset(self): - self.number = 0 - self.ANIMALS = NameGenerator.ANIMALS.copy() + ANIMALS = ANIMALS - def next_unit_name(self, country, parent_base_id, unit_type): - self.number += 1 - return "unit|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type)) + @classmethod + def reset(cls): + cls.number = 0 + cls.infantry_number = 0 + cls.ANIMALS = ANIMALS - def next_infantry_name(self, country, parent_base_id, unit_type): - self.number += 1 - return "infantry|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type)) + @classmethod + def reset_numbers(cls): + cls.number = 0 + cls.infantry_number = 0 + cls.aircraft_number = 0 - def next_basedefense_name(self): + @classmethod + def next_aircraft_name(cls, country: Country, parent_base_id: int, flight: Flight): + cls.aircraft_number += 1 + try: + if flight.custom_name: + name_str = flight.custom_name + else: + name_str = "{} {}".format( + flight.package.target.name, flight.flight_type) + except AttributeError: # Here to maintain save compatibility with 2.3 + name_str = "{} {}".format( + flight.package.target.name, flight.flight_type) + return "{}|{}|{}|{}|{}|".format(name_str, country.id, cls.aircraft_number, parent_base_id, db.unit_type_name(flight.unit_type)) + + @classmethod + def next_unit_name(cls, country: Country, parent_base_id: int, unit_type: UnitType): + cls.number += 1 + return "unit|{}|{}|{}|{}|".format(country.id, cls.number, parent_base_id, db.unit_type_name(unit_type)) + + @classmethod + def next_infantry_name(cls, country: Country, parent_base_id: int, unit_type: UnitType): + cls.infantry_number += 1 + return "infantry|{}|{}|{}|{}|".format(country.id, cls.infantry_number, parent_base_id, db.unit_type_name(unit_type)) + + @staticmethod + def next_basedefense_name(): return "basedefense_aa|0|0|" - def next_awacs_name(self, country): - self.number += 1 - return "awacs|{}|{}|0|".format(country.id, self.number) + @classmethod + def next_awacs_name(cls, country: Country): + cls.number += 1 + return "awacs|{}|{}|0|".format(country.id, cls.number) - def next_tanker_name(self, country, unit_type): - self.number += 1 - return "tanker|{}|{}|0|{}".format(country.id, self.number, db.unit_type_name(unit_type)) + @classmethod + def next_tanker_name(cls, country: Country, unit_type: UnitType): + cls.number += 1 + return "tanker|{}|{}|0|{}".format(country.id, cls.number, db.unit_type_name(unit_type)) - def next_carrier_name(self, country): - self.number += 1 - return "carrier|{}|{}|0|".format(country.id, self.number) + @classmethod + def next_carrier_name(cls, country: Country): + cls.number += 1 + return "carrier|{}|{}|0|".format(country.id, cls.number) - def random_objective_name(self): - if len(self.ANIMALS) == 0: + @classmethod + def random_objective_name(cls): + if len(cls.ANIMALS) == 0: return random.choice(ALPHA_MILITARY).upper() + "#" + str(random.randint(0, 100)) else: - animal = random.choice(self.ANIMALS) - self.ANIMALS.remove(animal) + animal = random.choice(cls.ANIMALS) + cls.ANIMALS.remove(animal) return animal -namegen = NameGenerator() - - - +namegen = NameGenerator diff --git a/qt_ui/widgets/ato.py b/qt_ui/widgets/ato.py index c4c38e22..11cff960 100644 --- a/qt_ui/widgets/ato.py +++ b/qt_ui/widgets/ato.py @@ -65,7 +65,7 @@ class FlightDelegate(QStyledItemDelegate): name = db.unit_type_name(flight.unit_type) estimator = TotEstimator(self.package) delay = estimator.mission_start_time(flight) - return f"[{task}] {count} x {name} in {delay}" + return f"{flight} in {delay}" def second_row_text(self, index: QModelIndex) -> str: flight = self.flight(index) diff --git a/qt_ui/windows/mission/flight/QFlightCreator.py b/qt_ui/windows/mission/flight/QFlightCreator.py index 0e0bf773..8066754f 100644 --- a/qt_ui/windows/mission/flight/QFlightCreator.py +++ b/qt_ui/windows/mission/flight/QFlightCreator.py @@ -1,3 +1,4 @@ +from re import L from typing import Optional from PySide2.QtCore import Qt, Signal @@ -6,6 +7,7 @@ from PySide2.QtWidgets import ( QMessageBox, QPushButton, QVBoxLayout, + QLineEdit, ) from dcs.planes import PlaneType @@ -31,6 +33,7 @@ class QFlightCreator(QDialog): self.game = game self.package = package + self.custom_name_text = None self.setWindowTitle("Create flight") self.setWindowIcon(EVENT_ICONS["strike"]) @@ -88,6 +91,12 @@ class QFlightCreator(QDialog): layout.addLayout( QLabeledWidget("Client Slots:", self.client_slots_spinner)) + self.custom_name = QLineEdit() + self.custom_name.textChanged.connect(self.set_custom_name_text) + layout.addLayout( + QLabeledWidget("Custom Flight Name (Optional)", self.custom_name) + ) + layout.addStretch() self.create_button = QPushButton("Create") @@ -96,6 +105,9 @@ class QFlightCreator(QDialog): self.setLayout(layout) + def set_custom_name_text(self, text: str): + self.custom_name_text = text + def verify_form(self) -> Optional[str]: aircraft: PlaneType = self.aircraft_selector.currentData() origin: ControlPoint = self.departure.currentData() @@ -115,6 +127,8 @@ class QFlightCreator(QDialog): return f"{origin.name} has only {available} {aircraft.id} available." if size <= 0: return f"Flight must have at least one aircraft." + if self.custom_name_text and "|" in self.custom_name_text: + return f"Cannot include | in flight name" return None def create_flight(self) -> None: @@ -141,7 +155,7 @@ class QFlightCreator(QDialog): else: start_type = "Warm" flight = Flight(self.package, aircraft, size, task, start_type, origin, - arrival, divert) + arrival, divert, custom_name=self.custom_name_text) flight.client_count = self.client_slots_spinner.value() # noinspection PyUnresolvedReferences diff --git a/qt_ui/windows/mission/flight/settings/QCustomName.py b/qt_ui/windows/mission/flight/settings/QCustomName.py new file mode 100644 index 00000000..7a22e48d --- /dev/null +++ b/qt_ui/windows/mission/flight/settings/QCustomName.py @@ -0,0 +1,16 @@ +from PySide2.QtWidgets import QGroupBox, QHBoxLayout, QLabel + +from gen.flights.flight import Flight + + +class QFlightCustomName(QGroupBox): + + def __init__(self, flight: Flight): + super(QFlightCustomName, self).__init__() + + self.flight = flight + + self.layout = QHBoxLayout() + self.custom_name_label = QLabel(f"Custom Name: {flight.custom_name}") + self.layout.addWidget(self.custom_name_label) + self.setLayout(self.layout) diff --git a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py index f1419669..71dfc036 100644 --- a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py +++ b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py @@ -1,5 +1,5 @@ from PySide2.QtCore import Signal -from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout +from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout, QLabel from game import Game from gen.ato import Package @@ -12,6 +12,8 @@ from qt_ui.windows.mission.flight.settings.QFlightStartType import \ QFlightStartType from qt_ui.windows.mission.flight.settings.QFlightTypeTaskInfo import \ QFlightTypeTaskInfo +from qt_ui.windows.mission.flight.settings.QCustomName import \ + QFlightCustomName class QGeneralFlightSettingsTab(QFrame): @@ -25,10 +27,12 @@ class QGeneralFlightSettingsTab(QFrame): flight_departure = QFlightDepartureDisplay(package, flight) flight_slots = QFlightSlotEditor(flight, game) flight_start_type = QFlightStartType(flight) + flight_custom_name = QFlightCustomName(flight) layout.addWidget(flight_info, 0, 0) layout.addWidget(flight_departure, 1, 0) layout.addWidget(flight_slots, 2, 0) layout.addWidget(flight_start_type, 3, 0) + layout.addWidget(flight_custom_name, 4, 0) vstretch = QVBoxLayout() vstretch.addStretch() layout.addLayout(vstretch, 3, 0)