diff --git a/changelog.md b/changelog.md
index cadbc6c4..de7568b2 100644
--- a/changelog.md
+++ b/changelog.md
@@ -13,6 +13,7 @@ Saves from 2.3 are not compatible with 2.4.
* **[Campaign AI]** Reserve aircraft will be ordered if needed to prioritize next turn's CAP/CAS over offensive missions.
* **[Campaign AI]** Multiple rounds of CAP will be planned (roughly 90 minutes of coverage). Default starting budget has increased to account for the increased need for aircraft.
* **[Mission Generator]** Multiple groups are created for complex SAM sites (SAMs with additional point defense or SHORADS), improving Skynet behavior.
+* **[Mission Generator]** Default start type can now be chosen in the settings. This replaces the non-functional "AI Parking Start" option. **Selecting any type other than cold will break OCA/Aircraft missions.**
* **[Cheat Menu]** Added ability to toggle base capture and frontline advance/retreat cheats.
* **[Skynet]** Updated to 2.0.1.
* **[Skynet]** Point defenses are now configured to remain on to protect the site they accompany.
diff --git a/game/settings.py b/game/settings.py
index a7253f25..d5872e91 100644
--- a/game/settings.py
+++ b/game/settings.py
@@ -19,11 +19,12 @@ class Settings:
supercarrier: bool = False
generate_marks: bool = True
manpads: bool = True
- cold_start: bool = False # Legacy parameter do not use
version: Optional[str] = None
player_income_multiplier: float = 1.0
enemy_income_multiplier: float = 1.0
+ default_start_type: str = "Cold"
+
# Campaign management
automate_runway_repair: bool = False
automate_front_line_reinforcements: bool = False
@@ -36,7 +37,6 @@ class Settings:
perf_artillery: bool = True
perf_moving_units: bool = True
perf_infantry: bool = True
- perf_ai_parking_start: bool = True
perf_destroyed_units: bool = True
# Performance culling
diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py
index f4479cf5..2267fcb4 100644
--- a/gen/flights/ai_flight_planner.py
+++ b/gen/flights/ai_flight_planner.py
@@ -563,15 +563,22 @@ class CoalitionMissionPlanner:
])
for target in self.objective_finder.oca_targets(min_aircraft=20):
- yield ProposedMission(target, [
- ProposedFlight(FlightType.OCA_AIRCRAFT, 2, self.MAX_OCA_RANGE),
+ flights = [
ProposedFlight(FlightType.OCA_RUNWAY, 2, self.MAX_OCA_RANGE),
+ ]
+ if self.game.settings.default_start_type == "Cold":
+ # Only schedule if the default start type is Cold. If the player
+ # has set anything else there are no targets to hit.
+ flights.append(ProposedFlight(FlightType.OCA_AIRCRAFT, 2,
+ self.MAX_OCA_RANGE))
+ flights.extend([
# TODO: Max escort range.
ProposedFlight(FlightType.ESCORT, 2, self.MAX_OCA_RANGE,
EscortType.AirToAir),
ProposedFlight(FlightType.SEAD, 2, self.MAX_OCA_RANGE,
EscortType.Sead),
])
+ yield ProposedMission(target, flights)
# Plan strike missions.
for target in self.objective_finder.strike_targets():
@@ -651,11 +658,6 @@ class CoalitionMissionPlanner:
reserves: bool = False) -> None:
"""Allocates aircraft for a proposed mission and adds it to the ATO."""
- if self.game.settings.perf_ai_parking_start:
- start_type = "Cold"
- else:
- start_type = "Warm"
-
if self.is_player:
package_country = self.game.player_country
else:
@@ -667,7 +669,7 @@ class CoalitionMissionPlanner:
self.game.aircraft_inventory,
self.is_player,
package_country,
- start_type
+ self.game.settings.default_start_type
)
# Attempt to plan all the main elements of the mission first. Escorts
diff --git a/qt_ui/windows/mission/flight/QFlightCreator.py b/qt_ui/windows/mission/flight/QFlightCreator.py
index 629dfad7..533ff71b 100644
--- a/qt_ui/windows/mission/flight/QFlightCreator.py
+++ b/qt_ui/windows/mission/flight/QFlightCreator.py
@@ -155,12 +155,11 @@ class QFlightCreator(QDialog):
if isinstance(origin, OffMapSpawn):
start_type = "In Flight"
- elif self.game.settings.perf_ai_parking_start:
- start_type = "Cold"
else:
- start_type = "Warm"
- flight = Flight(self.package, self.country, aircraft, size, task, start_type, origin,
- arrival, divert, custom_name=self.custom_name_text)
+ start_type = self.game.settings.default_start_type
+ flight = Flight(self.package, self.country, aircraft, size, task,
+ start_type, origin, 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/QFlightSlotEditor.py b/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py
index 1ecb9a28..f21b07b0 100644
--- a/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py
+++ b/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py
@@ -27,14 +27,14 @@ class QFlightSlotEditor(QGroupBox):
layout = QGridLayout()
- self.aircraft_count = QLabel("Aircraft count :")
+ self.aircraft_count = QLabel("Aircraft count:")
self.aircraft_count_spinner = QSpinBox()
self.aircraft_count_spinner.setMinimum(1)
self.aircraft_count_spinner.setMaximum(max_count)
self.aircraft_count_spinner.setValue(flight.count)
self.aircraft_count_spinner.valueChanged.connect(self._changed_aircraft_count)
- self.client_count = QLabel("Client slots count :")
+ self.client_count = QLabel("Client slots count:")
self.client_count_spinner = QSpinBox()
self.client_count_spinner.setMinimum(0)
self.client_count_spinner.setMaximum(max_count)
diff --git a/qt_ui/windows/mission/flight/settings/QFlightStartType.py b/qt_ui/windows/mission/flight/settings/QFlightStartType.py
index b49fb5d7..17fbe042 100644
--- a/qt_ui/windows/mission/flight/settings/QFlightStartType.py
+++ b/qt_ui/windows/mission/flight/settings/QFlightStartType.py
@@ -1,4 +1,10 @@
-from PySide2.QtWidgets import QComboBox, QGroupBox, QHBoxLayout, QLabel
+from PySide2.QtWidgets import (
+ QComboBox,
+ QGroupBox,
+ QHBoxLayout,
+ QLabel,
+ QVBoxLayout,
+)
from gen.flights.flight import Flight
from qt_ui.models import PackageModel
@@ -11,8 +17,9 @@ class QFlightStartType(QGroupBox):
self.package_model = package_model
self.flight = flight
- self.layout = QHBoxLayout()
- self.start_type_label = QLabel("Start type : ")
+ self.layout = QVBoxLayout()
+ self.main_row = QHBoxLayout()
+ self.start_type_label = QLabel("Start type:")
self.start_type = QComboBox()
for i, st in enumerate([b for b in ["Cold", "Warm", "Runway", "In Flight"]]):
@@ -21,11 +28,17 @@ class QFlightStartType(QGroupBox):
self.start_type.setCurrentIndex(i)
self.start_type.currentTextChanged.connect(self._on_start_type_selected)
- self.layout.addWidget(self.start_type_label)
- self.layout.addWidget(self.start_type)
+ self.main_row.addWidget(self.start_type_label)
+ self.main_row.addWidget(self.start_type)
+
+ self.layout.addLayout(self.main_row)
+ self.layout.addWidget(QLabel(
+ "Any option other than Cold will make this flight non-targetable " +
+ "by OCA/Aircraft missions. This will affect game balance."
+ ))
self.setLayout(self.layout)
def _on_start_type_selected(self):
selected = self.start_type.currentData()
self.flight.start_type = selected
- self.package_model.update_tot()
\ No newline at end of file
+ self.package_model.update_tot()
diff --git a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py
index 5875a79d..05eaf1fa 100644
--- a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py
+++ b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py
@@ -2,7 +2,6 @@ from PySide2.QtCore import Signal
from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout
from game import Game
-from gen.ato import Package
from gen.flights.flight import Flight
from qt_ui.models import PackageModel
from qt_ui.windows.mission.flight.settings.QFlightDepartureDisplay import \
@@ -24,21 +23,12 @@ class QGeneralFlightSettingsTab(QFrame):
super().__init__()
layout = QGridLayout()
- flight_info = QFlightTypeTaskInfo(flight)
- flight_departure = QFlightDepartureDisplay(package_model, flight)
- flight_slots = QFlightSlotEditor(package_model, flight, game)
- flight_start_type = QFlightStartType(package_model, 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)
+ layout.addWidget(QFlightTypeTaskInfo(flight), 0, 0)
+ layout.addWidget(QFlightDepartureDisplay(package_model, flight), 1, 0)
+ layout.addWidget(QFlightSlotEditor(package_model, flight, game), 2, 0)
+ layout.addWidget(QFlightStartType(package_model, flight), 3, 0)
+ layout.addWidget(QFlightCustomName(flight), 4, 0)
vstretch = QVBoxLayout()
vstretch.addStretch()
- layout.addLayout(vstretch, 3, 0)
+ layout.addLayout(vstretch, 5, 0)
self.setLayout(layout)
-
- flight_start_type.setEnabled(flight.client_count > 0)
- flight_slots.changed.connect(
- lambda: flight_start_type.setEnabled(flight.client_count > 0))
diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py
index d372214b..dad76e8d 100644
--- a/qt_ui/windows/settings/QSettingsWindow.py
+++ b/qt_ui/windows/settings/QSettingsWindow.py
@@ -23,6 +23,7 @@ from dcs.forcedoptions import ForcedOptions
import qt_ui.uiconstants as CONST
from game.game import Game
from game.infos.information import Information
+from game.settings import Settings
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
from qt_ui.widgets.spinsliders import TenthsSpinSlider
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
@@ -68,6 +69,21 @@ class CheatSettingsBox(QGroupBox):
return self.base_capture_cheat_checkbox.isChecked()
+START_TYPE_TOOLTIP = "Selects the start type used for AI aircraft."
+
+
+class StartTypeComboBox(QComboBox):
+ def __init__(self, settings: Settings) -> None:
+ super().__init__()
+ self.settings = settings
+ self.addItems(["Cold", "Warm", "Runway", "In Flight"])
+ self.currentTextChanged.connect(self.on_change)
+ self.setToolTip(START_TYPE_TOOLTIP)
+
+ def on_change(self, value: str) -> None:
+ self.settings.default_start_type = value
+
+
class QSettingsWindow(QDialog):
def __init__(self, game: Game):
@@ -370,6 +386,17 @@ class QSettingsWindow(QDialog):
self.gameplayLayout.addWidget(self.never_delay_players, 2, 1,
Qt.AlignRight)
+ start_type_label = QLabel(
+ "Default start type for AI aircraft:
Warning: " +
+ "Any option other than Cold breaks OCA/Aircraft missions."
+ )
+ start_type_label.setToolTip(START_TYPE_TOOLTIP)
+ start_type = StartTypeComboBox(self.game.settings)
+ start_type.setCurrentText(self.game.settings.default_start_type)
+
+ self.gameplayLayout.addWidget(start_type_label, 3, 0)
+ self.gameplayLayout.addWidget(start_type, 3, 1)
+
self.performance = QGroupBox("Performance")
self.performanceLayout = QGridLayout()
self.performanceLayout.setAlignment(Qt.AlignTop)
@@ -395,10 +422,6 @@ class QSettingsWindow(QDialog):
self.infantry.setChecked(self.game.settings.perf_infantry)
self.infantry.toggled.connect(self.applySettings)
- self.ai_parking_start = QCheckBox()
- self.ai_parking_start.setChecked(self.game.settings.perf_ai_parking_start)
- self.ai_parking_start.toggled.connect(self.applySettings)
-
self.destroyed_units = QCheckBox()
self.destroyed_units.setChecked(self.game.settings.perf_destroyed_units)
self.destroyed_units.toggled.connect(self.applySettings)
@@ -427,8 +450,6 @@ class QSettingsWindow(QDialog):
self.performanceLayout.addWidget(self.moving_units, 3, 1, alignment=Qt.AlignRight)
self.performanceLayout.addWidget(QLabel("Generate infantry squads along vehicles"), 4, 0)
self.performanceLayout.addWidget(self.infantry, 4, 1, alignment=Qt.AlignRight)
- self.performanceLayout.addWidget(QLabel("AI planes parking start (AI starts in flight if disabled)"), 5, 0)
- self.performanceLayout.addWidget(self.ai_parking_start, 5, 1, alignment=Qt.AlignRight)
self.performanceLayout.addWidget(QLabel("Include destroyed units carcass"), 6, 0)
self.performanceLayout.addWidget(self.destroyed_units, 6, 1, alignment=Qt.AlignRight)
@@ -504,7 +525,6 @@ class QSettingsWindow(QDialog):
self.game.settings.perf_artillery = self.arti.isChecked()
self.game.settings.perf_moving_units = self.moving_units.isChecked()
self.game.settings.perf_infantry = self.infantry.isChecked()
- self.game.settings.perf_ai_parking_start = self.ai_parking_start.isChecked()
self.game.settings.perf_destroyed_units = self.destroyed_units.isChecked()
self.game.settings.perf_culling = self.culling.isChecked()