mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Ability to set callsigns
This commit is contained in:
parent
aaf23d52bc
commit
ade81f4548
@ -24,6 +24,8 @@
|
||||
* **[Options]** New options in Settings: Spawn ground power trucks at ground starts in airbases/roadbases
|
||||
* **[Options]** Option for hiding TGOs (with IADS roles) on MFD
|
||||
* **[Plugins]** Splash Damage 2.1 with Clusters and Ship Radar effects.
|
||||
* **[COMMs]** Aircraft-specific callsigns will now also be used.
|
||||
* **[COMMs]** Ability to set a specific callsign to a flight.
|
||||
|
||||
## Fixes
|
||||
* **[Mission Generation]** Anti-ship strikes should use "group attack" in their attack-task
|
||||
|
||||
@ -14,7 +14,9 @@ from .flightmembers import FlightMembers
|
||||
from .flightroster import FlightRoster
|
||||
from .flightstate import FlightState, Navigating, Uninitialized
|
||||
from .flightstate.killed import Killed
|
||||
from .flighttype import FlightType
|
||||
from .loadouts import Weapon
|
||||
from ..radio.CallsignContainer import CallsignContainer
|
||||
from ..radio.RadioFrequencyContainer import RadioFrequencyContainer
|
||||
from ..radio.TacanContainer import TacanContainer
|
||||
from ..radio.radios import RadioFrequency
|
||||
@ -36,7 +38,6 @@ if TYPE_CHECKING:
|
||||
from game.data.weapons import WeaponType
|
||||
from .flightmember import FlightMember
|
||||
from .flightplans.flightplan import FlightPlan
|
||||
from .flighttype import FlightType
|
||||
from .flightwaypoint import FlightWaypoint
|
||||
from .package import Package
|
||||
from .starttype import StartType
|
||||
@ -44,7 +45,9 @@ if TYPE_CHECKING:
|
||||
F18_TGP_PYLON: int = 4
|
||||
|
||||
|
||||
class Flight(SidcDescribable, RadioFrequencyContainer, TacanContainer):
|
||||
class Flight(
|
||||
SidcDescribable, RadioFrequencyContainer, TacanContainer, CallsignContainer
|
||||
):
|
||||
def __init__(
|
||||
self,
|
||||
package: Package,
|
||||
@ -58,7 +61,7 @@ class Flight(SidcDescribable, RadioFrequencyContainer, TacanContainer):
|
||||
roster: Optional[FlightRoster] = None,
|
||||
frequency: Optional[RadioFrequency] = None,
|
||||
channel: Optional[TacanChannel] = None,
|
||||
callsign: Optional[str] = None,
|
||||
callsign_tcn: Optional[str] = None,
|
||||
claim_inv: bool = True,
|
||||
) -> None:
|
||||
self.id = uuid.uuid4()
|
||||
@ -81,7 +84,7 @@ class Flight(SidcDescribable, RadioFrequencyContainer, TacanContainer):
|
||||
self.frequency = frequency
|
||||
if self.unit_type.dcs_unit_type.tacan:
|
||||
self.tacan = channel
|
||||
self.tcn_name = callsign
|
||||
self.tcn_name = callsign_tcn
|
||||
|
||||
self.initialize_fuel()
|
||||
self.use_same_loadout_for_all_members = True
|
||||
@ -120,6 +123,22 @@ class Flight(SidcDescribable, RadioFrequencyContainer, TacanContainer):
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def available_callsigns(self) -> List[str]:
|
||||
callsigns = set()
|
||||
dcs_unit = self.squadron.aircraft.dcs_unit_type
|
||||
category = dcs_unit.category
|
||||
category = "Air" if category == "Interceptor" else category
|
||||
for name in self.squadron.coalition.faction.country.callsign[category]:
|
||||
callsigns.add(name)
|
||||
if hasattr(dcs_unit, "callnames"):
|
||||
country_name = self.squadron.coalition.faction.country.name
|
||||
for c in dcs_unit.callnames:
|
||||
if "Combined Joint Task Forces" in country_name or c == country_name:
|
||||
for name in dcs_unit.callnames[c]:
|
||||
callsigns.add(name)
|
||||
return sorted(callsigns)
|
||||
|
||||
@property
|
||||
def flight_plan(self) -> FlightPlan[Any]:
|
||||
return self._flight_plan_builder.get_or_build()
|
||||
|
||||
@ -266,6 +266,8 @@ class FlightGroupSpawner:
|
||||
start_type=self._start_type_at_airfield(airfield),
|
||||
group_size=self.flight.count,
|
||||
parking_slots=None,
|
||||
callsign_name=self.flight.callsign.name if self.flight.callsign else None,
|
||||
callsign_nr=self.flight.callsign.nr if self.flight.callsign else None,
|
||||
)
|
||||
|
||||
def _generate_over_departure(
|
||||
@ -299,6 +301,8 @@ class FlightGroupSpawner:
|
||||
speed=speed.kph,
|
||||
maintask=None,
|
||||
group_size=self.flight.count,
|
||||
callsign_name=self.flight.callsign.name if self.flight.callsign else None,
|
||||
callsign_nr=self.flight.callsign.nr if self.flight.callsign else None,
|
||||
)
|
||||
|
||||
group.points[0].alt_type = alt_type
|
||||
@ -315,6 +319,8 @@ class FlightGroupSpawner:
|
||||
maintask=None,
|
||||
start_type=self._start_type_at_group(at),
|
||||
group_size=self.flight.count,
|
||||
callsign_name=self.flight.callsign.name if self.flight.callsign else None,
|
||||
callsign_nr=self.flight.callsign.nr if self.flight.callsign else None,
|
||||
)
|
||||
|
||||
def _generate_at_cp_helipad(
|
||||
|
||||
20
game/radio/CallsignContainer.py
Normal file
20
game/radio/CallsignContainer.py
Normal file
@ -0,0 +1,20 @@
|
||||
from abc import abstractmethod
|
||||
from typing import Optional, List
|
||||
|
||||
|
||||
class Callsign:
|
||||
name: Optional[str] = None
|
||||
nr: Optional[int] = None
|
||||
|
||||
def __init__(self, name: Optional[str], nr: int) -> None:
|
||||
self.name = name
|
||||
self.nr = nr
|
||||
|
||||
|
||||
class CallsignContainer:
|
||||
callsign: Optional[Callsign] = None
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def available_callsigns(self) -> List[str]:
|
||||
...
|
||||
70
qt_ui/widgets/QCallsignWidget.py
Normal file
70
qt_ui/widgets/QCallsignWidget.py
Normal file
@ -0,0 +1,70 @@
|
||||
from PySide6.QtCore import Signal
|
||||
from PySide6.QtWidgets import (
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from game.radio.CallsignContainer import CallsignContainer, Callsign
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.windows.QCallsignDialog import QCallsignDialog
|
||||
|
||||
|
||||
class QCallsignWidget(QWidget):
|
||||
callsign_changed = Signal(QWidget)
|
||||
|
||||
def __init__(self, container: CallsignContainer, game_model: GameModel) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.ct = container
|
||||
self.gm = game_model
|
||||
|
||||
columns = QHBoxLayout()
|
||||
self.setLayout(columns)
|
||||
|
||||
self.callsign = QLabel(self._get_label_text())
|
||||
columns.addWidget(self.callsign)
|
||||
columns.addStretch()
|
||||
|
||||
self.set_callsign_btn = QPushButton("Set Callsign")
|
||||
self.set_callsign_btn.setProperty("class", "comms")
|
||||
self.set_callsign_btn.setFixedWidth(100)
|
||||
columns.addWidget(self.set_callsign_btn)
|
||||
self.set_callsign_btn.clicked.connect(self.open_callsign_dialog)
|
||||
|
||||
self.reset_callsign_btn = QPushButton("Reset Callsign")
|
||||
self.reset_callsign_btn.setProperty("class", "btn-danger comms")
|
||||
self.reset_callsign_btn.setFixedWidth(100)
|
||||
columns.addWidget(self.reset_callsign_btn)
|
||||
self.reset_callsign_btn.clicked.connect(self.reset_callsign)
|
||||
|
||||
def _get_label_text(self) -> str:
|
||||
cs = (
|
||||
"AUTO"
|
||||
if self.ct.callsign is None
|
||||
else f"{self.ct.callsign.name} {self.ct.callsign.nr}"
|
||||
)
|
||||
return f"<b>Callsign: {cs}</b>"
|
||||
|
||||
def open_callsign_dialog(self) -> None:
|
||||
self.callsign_dialog = QCallsignDialog(self, self.ct)
|
||||
self.callsign_dialog.accepted.connect(self.assign_callsign)
|
||||
self.callsign_dialog.show()
|
||||
|
||||
def assign_callsign(self) -> None:
|
||||
name = self.callsign_dialog.callsign_name_input.currentText()
|
||||
nr = self.callsign_dialog.callsign_nr_input.value()
|
||||
self.ct.callsign = Callsign(name, nr)
|
||||
self.callsign.setText(self._get_label_text())
|
||||
self.callsign_changed.emit(self)
|
||||
|
||||
def reset_callsign(self) -> None:
|
||||
self.ct.callsign = None
|
||||
self.callsign.setText(self._get_label_text())
|
||||
self._reset_color_and_tooltip()
|
||||
self.callsign_changed.emit(self)
|
||||
|
||||
def _reset_color_and_tooltip(self):
|
||||
self.callsign.setStyleSheet("color: white")
|
||||
self.callsign.setToolTip(None)
|
||||
56
qt_ui/windows/QCallsignDialog.py
Normal file
56
qt_ui/windows/QCallsignDialog.py
Normal file
@ -0,0 +1,56 @@
|
||||
from typing import Optional
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog,
|
||||
QPushButton,
|
||||
QLabel,
|
||||
QHBoxLayout,
|
||||
QSpinBox,
|
||||
QComboBox,
|
||||
)
|
||||
|
||||
from game.radio.CallsignContainer import CallsignContainer
|
||||
from qt_ui.uiconstants import EVENT_ICONS
|
||||
|
||||
|
||||
class QCallsignDialog(QDialog):
|
||||
def __init__(
|
||||
self, parent=None, container: Optional[CallsignContainer] = None
|
||||
) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self.container = container
|
||||
self.setMinimumWidth(400)
|
||||
|
||||
# Make dialog modal to prevent background windows to close unexpectedly.
|
||||
self.setModal(True)
|
||||
|
||||
self.setWindowTitle("Assign Callsign")
|
||||
self.setWindowIcon(EVENT_ICONS["strike"])
|
||||
|
||||
layout = QHBoxLayout()
|
||||
|
||||
self.callsign_label = QLabel("Callsign:")
|
||||
self.callsign_name_input = QComboBox()
|
||||
self.callsign_name_input.addItems(container.available_callsigns)
|
||||
self.callsign_nr_input = QSpinBox()
|
||||
self.callsign_nr_input.setRange(1, 9)
|
||||
self.callsign_nr_input.setSingleStep(1)
|
||||
self.callsign_nr_input.setMaximumWidth(50)
|
||||
layout.addWidget(self.callsign_label)
|
||||
layout.addStretch()
|
||||
layout.addWidget(self.callsign_name_input)
|
||||
layout.addStretch()
|
||||
layout.addWidget(self.callsign_nr_input)
|
||||
layout.addStretch()
|
||||
|
||||
self.create_button = QPushButton("Save")
|
||||
self.create_button.clicked.connect(self.accept)
|
||||
layout.addWidget(self.create_button, alignment=Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
if container is not None:
|
||||
if container.callsign is not None:
|
||||
self.callsign_name_input.setCurrentText(container.callsign.name)
|
||||
self.callsign_nr_input.setValue(container.callsign.nr)
|
||||
@ -2,13 +2,14 @@ from PySide6.QtWidgets import QGroupBox, QVBoxLayout
|
||||
|
||||
from game.ato import Flight, FlightType
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.widgets.QCallsignWidget import QCallsignWidget
|
||||
from qt_ui.widgets.QFrequencyWidget import QFrequencyWidget
|
||||
from qt_ui.widgets.QTacanWidget import QTacanWidget
|
||||
|
||||
|
||||
class QCommsEditor(QGroupBox):
|
||||
def __init__(self, flight: Flight, game: GameModel):
|
||||
title = "Intra-Flight Frequency"
|
||||
title = "COMMs"
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
@ -16,9 +17,9 @@ class QCommsEditor(QGroupBox):
|
||||
has_tacan = flight.unit_type.dcs_unit_type.tacan
|
||||
|
||||
layout.addWidget(QFrequencyWidget(flight, game))
|
||||
layout.addWidget(QCallsignWidget(flight, game))
|
||||
if is_refuel and has_tacan:
|
||||
layout.addWidget(QTacanWidget(flight, game))
|
||||
title = title + " / TACAN"
|
||||
super(QCommsEditor, self).__init__(title)
|
||||
self.flight = flight
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user