mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Babysteps towards full OPFOR control
This commit is contained in:
parent
b61f625828
commit
f203a5cf7a
@ -13,13 +13,13 @@
|
||||
* **[Options]** Extend option (so it can be disabled when fixed in DCS) to force air-starts (except for the slots that work) at Ramon Airbase, similar to the Nevatim fix in Retribution 1.3.0
|
||||
* **[Options]** New option in Settings: Default start type for Player flights.
|
||||
* **[AirWing]** Expose OPFOR Squadrons, giving the ability to change liveries, auto-assignable mission types & an easy way to retrieve debug information.
|
||||
|
||||
* **[ATO]** Allow planning as OPFOR
|
||||
*
|
||||
## Fixes
|
||||
* **[UI/UX]** A-10A flights can be edited again.
|
||||
* **[Mission Generation]** IADS bug sometimes triggering "no skynet usable units" error during mission generation
|
||||
* **[New Game Wizard]** Campaign errors show a dialog again and avoid CTDs
|
||||
|
||||
|
||||
# Retribution v1.3.1
|
||||
#### Note: Re-save your missions in DCS' Mission Editor to avoid possible crashes due to datalink (usually the case when F-16C blk50s are used) when hosting missions on a dedicated server.
|
||||
|
||||
|
||||
@ -1036,7 +1036,6 @@ class Settings:
|
||||
|
||||
# Cheating. Not using auto settings because the same page also has buttons which do
|
||||
# not alter settings.
|
||||
show_red_ato: bool = False
|
||||
enable_frontline_cheats: bool = False
|
||||
enable_base_capture_cheat: bool = False
|
||||
enable_transfer_cheat: bool = False
|
||||
|
||||
@ -37,7 +37,7 @@ class Dialog:
|
||||
def open_new_package_dialog(cls, mission_target: MissionTarget, parent=None):
|
||||
"""Opens the dialog to create a new package with the given target."""
|
||||
cls.new_package_dialog = QNewPackageDialog(
|
||||
cls.game_model, cls.game_model.ato_model, mission_target, parent=parent
|
||||
cls.game_model, mission_target, parent=parent
|
||||
)
|
||||
cls.new_package_dialog.show()
|
||||
|
||||
|
||||
@ -547,6 +547,7 @@ class GameModel:
|
||||
else:
|
||||
self.ato_model = AtoModel(self, self.game.blue.ato)
|
||||
self.red_ato_model = AtoModel(self, self.game.red.ato)
|
||||
self.is_ownfor = False
|
||||
|
||||
# For UI purposes
|
||||
self.allocated_freqs: list[RadioFrequency] = list()
|
||||
|
||||
@ -23,12 +23,14 @@ from PySide6.QtWidgets import (
|
||||
QSplitter,
|
||||
QVBoxLayout,
|
||||
QMessageBox,
|
||||
QCheckBox,
|
||||
)
|
||||
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.package import Package
|
||||
from game.server import EventStream
|
||||
from game.sim import GameUpdateEvents
|
||||
from .QLabeledWidget import QLabeledWidget
|
||||
from ..delegates import TwoColumnRowDelegate
|
||||
from ..models import AtoModel, GameModel, NullListModel, PackageModel
|
||||
|
||||
@ -484,8 +486,20 @@ class QAirTaskingOrderPanel(QSplitter):
|
||||
|
||||
def __init__(self, game_model: GameModel) -> None:
|
||||
super().__init__(Qt.Orientation.Vertical)
|
||||
self.game_model = game_model
|
||||
self.ato_model = game_model.ato_model
|
||||
|
||||
# ATO
|
||||
self.red_ato_checkbox = QCheckBox()
|
||||
self.red_ato_checkbox.toggled.connect(self.on_ato_changed)
|
||||
self.red_ato_labeled = QLabeledWidget(
|
||||
"Show/Plan OPFOR's ATO: ", self.red_ato_checkbox
|
||||
)
|
||||
|
||||
self.ato_group_box = QGroupBox("ATO")
|
||||
self.ato_group_box.setLayout(self.red_ato_labeled)
|
||||
self.addWidget(self.ato_group_box)
|
||||
|
||||
self.package_panel = QPackagePanel(game_model, self.ato_model)
|
||||
self.package_panel.current_changed.connect(self.on_package_change)
|
||||
self.addWidget(self.package_panel)
|
||||
@ -500,3 +514,17 @@ class QAirTaskingOrderPanel(QSplitter):
|
||||
self.flight_panel.set_package(self.ato_model.get_package_model(index))
|
||||
else:
|
||||
self.flight_panel.set_package(None)
|
||||
|
||||
def on_ato_changed(self) -> None:
|
||||
opfor = self.red_ato_checkbox.isChecked()
|
||||
ato_model = (
|
||||
self.game_model.red_ato_model if opfor else self.game_model.ato_model
|
||||
)
|
||||
ato_model.layoutChanged.connect(self.package_panel.on_current_changed)
|
||||
self.ato_model = ato_model
|
||||
self.package_panel.ato_model = ato_model
|
||||
self.package_panel.package_list.ato_model = ato_model
|
||||
self.package_panel.package_list.setModel(ato_model)
|
||||
self.package_panel.current_changed.connect(self.on_package_change)
|
||||
self.flight_panel.flight_list.set_package(None)
|
||||
self.game_model.is_ownfor = not opfor
|
||||
|
||||
@ -11,12 +11,16 @@ class QFlightTypeComboBox(QComboBox):
|
||||
"""Combo box for selecting a flight task type."""
|
||||
|
||||
def __init__(
|
||||
self, theater: ConflictTheater, target: MissionTarget, settings: Settings
|
||||
self,
|
||||
theater: ConflictTheater,
|
||||
target: MissionTarget,
|
||||
settings: Settings,
|
||||
is_ownfor: bool,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.theater = theater
|
||||
self.target = target
|
||||
for mission_type in self.target.mission_types(for_player=True):
|
||||
for mission_type in self.target.mission_types(for_player=is_ownfor):
|
||||
if mission_type == FlightType.AIR_ASSAULT and not settings.plugin_option(
|
||||
"ctld"
|
||||
):
|
||||
|
||||
@ -15,6 +15,7 @@ class GameUpdateSignal(QObject):
|
||||
budgetupdated = Signal(Game)
|
||||
game_state_changed = Signal(TurnState)
|
||||
debriefingReceived = Signal(Debriefing)
|
||||
ato_changed = Signal()
|
||||
|
||||
game_loaded = Signal(Game)
|
||||
|
||||
@ -41,6 +42,9 @@ class GameUpdateSignal(QObject):
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.game_state_changed.emit(state)
|
||||
|
||||
def atoChanged(self) -> None:
|
||||
self.ato_changed.emit()
|
||||
|
||||
@staticmethod
|
||||
def get_instance() -> GameUpdateSignal:
|
||||
return GameUpdateSignal.instance
|
||||
|
||||
@ -31,11 +31,14 @@ def _spinbox_template() -> QSpinBox:
|
||||
|
||||
|
||||
class QAutoCreateDialog(QDialog):
|
||||
def __init__(self, game: Game, model: PackageModel, parent=None) -> None:
|
||||
def __init__(
|
||||
self, game: Game, model: PackageModel, is_ownfor: bool, parent=None
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
self.game = game
|
||||
self.package_model = model
|
||||
self.package = model.package
|
||||
self.is_ownfor = is_ownfor
|
||||
|
||||
self.setMinimumSize(300, 400)
|
||||
self.setWindowTitle(
|
||||
@ -159,7 +162,7 @@ class QAutoCreateDialog(QDialog):
|
||||
FlightType.BAI,
|
||||
FlightType.CAS,
|
||||
}
|
||||
for mt in self.package.target.mission_types(True):
|
||||
for mt in self.package.target.mission_types(self.is_ownfor):
|
||||
if mt in primary_tasks:
|
||||
self.primary_combobox.addItem(mt.value, mt)
|
||||
self.primary_combobox.setCurrentIndex(0)
|
||||
@ -172,15 +175,16 @@ class QAutoCreateDialog(QDialog):
|
||||
return cb
|
||||
|
||||
def _create_type_selector(self, flight_type: FlightType) -> QComboBox:
|
||||
airwing = self.game.blue.air_wing
|
||||
air_wing = self.game.blue.air_wing if self.is_ownfor else self.game.red.air_wing
|
||||
cb = QComboBox()
|
||||
for ac in airwing.best_available_aircrafts_for(flight_type):
|
||||
for ac in air_wing.best_available_aircrafts_for(flight_type):
|
||||
cb.addItem(ac.variant_id, ac)
|
||||
return cb
|
||||
|
||||
def _load_aircraft_types(self):
|
||||
self.primary_type.clear()
|
||||
for ac in self.game.blue.air_wing.best_available_aircrafts_for(
|
||||
air_wing = self.game.blue.air_wing if self.is_ownfor else self.game.red.air_wing
|
||||
for ac in air_wing.best_available_aircrafts_for(
|
||||
self.primary_combobox.currentData()
|
||||
):
|
||||
self.primary_type.addItem(ac.variant_id, ac)
|
||||
@ -217,7 +221,7 @@ class QAutoCreateDialog(QDialog):
|
||||
with tracer.trace(f"Auto-plan package"):
|
||||
pm = ProposedMission(self.package.target, pf, asap=True)
|
||||
pff = PackageFulfiller(
|
||||
self.game.coalition_for(True),
|
||||
self.game.coalition_for(self.is_ownfor),
|
||||
self.game.theater,
|
||||
self.game.db.flights,
|
||||
self.game.settings,
|
||||
|
||||
@ -198,7 +198,10 @@ class QPackageDialog(QDialog):
|
||||
def on_add_flight(self) -> None:
|
||||
"""Opens the new flight dialog."""
|
||||
self.add_flight_dialog = QFlightCreator(
|
||||
self.game, self.package_model.package, parent=self.window()
|
||||
self.game,
|
||||
self.package_model.package,
|
||||
is_ownfor=self.game_model.is_ownfor,
|
||||
parent=self.window(),
|
||||
)
|
||||
self.add_flight_dialog.created.connect(self.add_flight)
|
||||
self.add_flight_dialog.show()
|
||||
@ -235,7 +238,10 @@ class QPackageDialog(QDialog):
|
||||
def on_auto_create(self) -> None:
|
||||
"""Opens the new flight dialog."""
|
||||
auto_create_dialog = QAutoCreateDialog(
|
||||
self.game, self.package_model, parent=self.window()
|
||||
self.game,
|
||||
self.package_model,
|
||||
self.game_model.is_ownfor,
|
||||
parent=self.window(),
|
||||
)
|
||||
if auto_create_dialog.exec_() == QDialog.DialogCode.Accepted:
|
||||
for f in self.package_model.package.flights:
|
||||
@ -275,7 +281,7 @@ class QNewPackageDialog(QPackageDialog):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, game_model: GameModel, model: AtoModel, target: MissionTarget, parent=None
|
||||
self, game_model: GameModel, target: MissionTarget, parent=None
|
||||
) -> None:
|
||||
super().__init__(
|
||||
game_model,
|
||||
@ -284,7 +290,9 @@ class QNewPackageDialog(QPackageDialog):
|
||||
),
|
||||
parent=parent,
|
||||
)
|
||||
self.ato_model = model
|
||||
self.ato_model = (
|
||||
game_model.ato_model if game_model.is_ownfor else game_model.red_ato_model
|
||||
)
|
||||
|
||||
# In the *new* package dialog, a package has been created and may have aircraft
|
||||
# assigned to it, but it is not a part of the ATO until the user saves it.
|
||||
|
||||
@ -33,7 +33,9 @@ from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import FlightRoster
|
||||
class QFlightCreator(QDialog):
|
||||
created = Signal(Flight)
|
||||
|
||||
def __init__(self, game: Game, package: Package, parent=None) -> None:
|
||||
def __init__(
|
||||
self, game: Game, package: Package, is_ownfor: bool, parent=None
|
||||
) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self.setMinimumWidth(400)
|
||||
|
||||
@ -50,23 +52,22 @@ class QFlightCreator(QDialog):
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.task_selector = QFlightTypeComboBox(
|
||||
self.game.theater, package.target, self.game.settings
|
||||
self.game.theater, package.target, self.game.settings, is_ownfor
|
||||
)
|
||||
self.task_selector.setCurrentIndex(0)
|
||||
self.task_selector.currentIndexChanged.connect(self.on_task_changed)
|
||||
layout.addLayout(QLabeledWidget("Task:", self.task_selector))
|
||||
|
||||
self.air_wing = self.game.blue.air_wing if is_ownfor else self.game.red.air_wing
|
||||
self.aircraft_selector = QAircraftTypeSelector(
|
||||
self.game.blue.air_wing.best_available_aircrafts_for(
|
||||
self.task_selector.currentData()
|
||||
)
|
||||
self.air_wing.best_available_aircrafts_for(self.task_selector.currentData())
|
||||
)
|
||||
self.aircraft_selector.setCurrentIndex(0)
|
||||
self.aircraft_selector.currentIndexChanged.connect(self.on_aircraft_changed)
|
||||
layout.addLayout(QLabeledWidget("Aircraft:", self.aircraft_selector))
|
||||
|
||||
self.squadron_selector = SquadronSelector(
|
||||
self.game.air_wing_for(player=True),
|
||||
self.air_wing,
|
||||
self.task_selector.currentData(),
|
||||
self.aircraft_selector.currentData(),
|
||||
)
|
||||
@ -74,7 +75,7 @@ class QFlightCreator(QDialog):
|
||||
layout.addLayout(QLabeledWidget("Squadron:", self.squadron_selector))
|
||||
|
||||
self.divert = QArrivalAirfieldSelector(
|
||||
[cp for cp in game.theater.controlpoints if cp.captured],
|
||||
[cp for cp in game.theater.controlpoints if cp.captured == is_ownfor],
|
||||
self.aircraft_selector.currentData(),
|
||||
"None",
|
||||
)
|
||||
@ -235,7 +236,7 @@ class QFlightCreator(QDialog):
|
||||
def on_task_changed(self, index: int) -> None:
|
||||
task = self.task_selector.itemData(index)
|
||||
self.aircraft_selector.update_items(
|
||||
self.game.blue.air_wing.best_available_aircrafts_for(task)
|
||||
self.air_wing.best_available_aircrafts_for(task)
|
||||
)
|
||||
self.squadron_selector.update_items(task, self.aircraft_selector.currentData())
|
||||
|
||||
|
||||
@ -54,13 +54,6 @@ class CheatSettingsBox(QGroupBox):
|
||||
self.main_layout = QVBoxLayout()
|
||||
self.setLayout(self.main_layout)
|
||||
|
||||
# ATO
|
||||
self.red_ato_checkbox = QCheckBox()
|
||||
self.red_ato_checkbox.setChecked(sc.settings.show_red_ato)
|
||||
self.red_ato_checkbox.toggled.connect(apply_settings)
|
||||
self.red_ato = QLabeledWidget("Show Red ATO:", self.red_ato_checkbox)
|
||||
self.main_layout.addLayout(self.red_ato)
|
||||
|
||||
# Frontline
|
||||
self.frontline_cheat_checkbox = QCheckBox()
|
||||
self.frontline_cheat_checkbox.setChecked(sc.settings.enable_frontline_cheats)
|
||||
@ -122,10 +115,6 @@ class CheatSettingsBox(QGroupBox):
|
||||
)
|
||||
self.main_layout.addLayout(self.redfor_buysell_cheat)
|
||||
|
||||
@property
|
||||
def show_red_ato(self) -> bool:
|
||||
return self.red_ato_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def show_frontline_cheat(self) -> bool:
|
||||
return self.frontline_cheat_checkbox.isChecked()
|
||||
@ -496,7 +485,6 @@ class QSettingsWidget(QtWidgets.QWizardPage, SettingsContainer):
|
||||
def applySettings(self):
|
||||
if self.updating_ui:
|
||||
return
|
||||
self.settings.show_red_ato = self.cheat_options.show_red_ato
|
||||
self.settings.enable_frontline_cheats = self.cheat_options.show_frontline_cheat
|
||||
self.settings.enable_base_capture_cheat = (
|
||||
self.cheat_options.show_base_capture_cheat
|
||||
@ -525,7 +513,6 @@ class QSettingsWidget(QtWidgets.QWizardPage, SettingsContainer):
|
||||
for p in self.pages.values():
|
||||
p.update_from_settings()
|
||||
|
||||
self.cheat_options.red_ato_checkbox.setChecked(self.settings.show_red_ato)
|
||||
self.cheat_options.base_capture_cheat_checkbox.setChecked(
|
||||
self.settings.enable_base_capture_cheat
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user