Babysteps towards full OPFOR control

This commit is contained in:
Raffson 2024-05-11 23:04:36 +02:00
parent b61f625828
commit f203a5cf7a
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
11 changed files with 73 additions and 37 deletions

View File

@ -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.

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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"
):

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -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())

View File

@ -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
)