mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add option for setting desired mission length.
This commit is contained in:
parent
747683e9e8
commit
56abd0bb7f
@ -7,6 +7,7 @@ Saves from 2.5 are not compatible with 3.0.
|
|||||||
* **[Campaign]** Ground units can now be transferred by road, airlift, and cargo ship. See https://github.com/dcs-liberation/dcs_liberation/wiki/Unit-Transfers for more information.
|
* **[Campaign]** Ground units can now be transferred by road, airlift, and cargo ship. See https://github.com/dcs-liberation/dcs_liberation/wiki/Unit-Transfers for more information.
|
||||||
* **[Campaign]** Ground units can no longer be sold. To move units to a new location, transfer them.
|
* **[Campaign]** Ground units can no longer be sold. To move units to a new location, transfer them.
|
||||||
* **[Campaign]** Ground units must now be recruited at a base with a factory and transferred to their destination. When buying units in the UI, the purchase will automatically be fulfilled at the closest factory, and a transfer will be created on the next turn.
|
* **[Campaign]** Ground units must now be recruited at a base with a factory and transferred to their destination. When buying units in the UI, the purchase will automatically be fulfilled at the closest factory, and a transfer will be created on the next turn.
|
||||||
|
* **[Campaign AI]** Every 30 minutes the AI will plan a CAP, so players can customize their mission better.
|
||||||
* **[UI]** Campaigns generated for an older or newer version of the game will now be marked as incompatible. They can still be played, but bugs may be present.
|
* **[UI]** Campaigns generated for an older or newer version of the game will now be marked as incompatible. They can still be played, but bugs may be present.
|
||||||
* **[Modding]** Campaigns now choose locations for factories to spawn.
|
* **[Modding]** Campaigns now choose locations for factories to spawn.
|
||||||
* **[Modding]** Can now install custom factions to <DCS saved games>/Liberation/Factions instead of the Liberation install directory.
|
* **[Modding]** Can now install custom factions to <DCS saved games>/Liberation/Factions instead of the Liberation install directory.
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import timedelta
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from dcs.forcedoptions import ForcedOptions
|
from dcs.forcedoptions import ForcedOptions
|
||||||
@ -25,6 +26,9 @@ class Settings:
|
|||||||
|
|
||||||
default_start_type: str = "Cold"
|
default_start_type: str = "Cold"
|
||||||
|
|
||||||
|
# Mission specific
|
||||||
|
desired_player_mission_duration: timedelta = timedelta(minutes=90)
|
||||||
|
|
||||||
# Campaign management
|
# Campaign management
|
||||||
automate_runway_repair: bool = False
|
automate_runway_repair: bool = False
|
||||||
automate_front_line_reinforcements: bool = False
|
automate_front_line_reinforcements: bool = False
|
||||||
|
|||||||
@ -583,26 +583,18 @@ class CoalitionMissionPlanner:
|
|||||||
|
|
||||||
# Find friendly CPs within 100 nmi from an enemy airfield, plan CAP.
|
# Find friendly CPs within 100 nmi from an enemy airfield, plan CAP.
|
||||||
for cp in self.objective_finder.vulnerable_control_points():
|
for cp in self.objective_finder.vulnerable_control_points():
|
||||||
# Plan three rounds of CAP to give ~90 minutes coverage. Spacing
|
# Plan CAP in such a way, that it is established during the whole desired mission length
|
||||||
# these out appropriately is done in stagger_missions.
|
for _ in range(
|
||||||
yield ProposedMission(
|
0,
|
||||||
cp,
|
int(self.game.settings.desired_player_mission_duration.total_seconds()),
|
||||||
[
|
int(self.faction.doctrine.cap_duration.total_seconds()),
|
||||||
ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE),
|
):
|
||||||
],
|
yield ProposedMission(
|
||||||
)
|
cp,
|
||||||
yield ProposedMission(
|
[
|
||||||
cp,
|
ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE),
|
||||||
[
|
],
|
||||||
ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE),
|
)
|
||||||
],
|
|
||||||
)
|
|
||||||
yield ProposedMission(
|
|
||||||
cp,
|
|
||||||
[
|
|
||||||
ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Find front lines, plan CAS.
|
# Find front lines, plan CAS.
|
||||||
for front_line in self.objective_finder.front_lines():
|
for front_line in self.objective_finder.front_lines():
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
from PySide2 import QtWidgets
|
||||||
from PySide2.QtCore import Qt
|
from PySide2.QtCore import Qt
|
||||||
from PySide2.QtWidgets import QGridLayout, QLabel, QSlider
|
from PySide2.QtWidgets import QGridLayout, QLabel, QSlider
|
||||||
|
from typing import Optional
|
||||||
from qt_ui.widgets.floatspinners import TenthsSpinner
|
from qt_ui.widgets.floatspinners import TenthsSpinner
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
class TenthsSpinSlider(QGridLayout):
|
class TenthsSpinSlider(QGridLayout):
|
||||||
@ -23,3 +25,68 @@ class TenthsSpinSlider(QGridLayout):
|
|||||||
@property
|
@property
|
||||||
def value(self) -> float:
|
def value(self) -> float:
|
||||||
return self.spinner.value() / 10
|
return self.spinner.value() / 10
|
||||||
|
|
||||||
|
|
||||||
|
class TimeInputs(QtWidgets.QGridLayout):
|
||||||
|
def __init__(self, label: str, initial: timedelta) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.addWidget(QtWidgets.QLabel(label), 0, 0)
|
||||||
|
|
||||||
|
minimum_minutes = 30
|
||||||
|
maximum_minutes = 150
|
||||||
|
initial_minutes = int(initial.total_seconds() / 60)
|
||||||
|
|
||||||
|
slider = QtWidgets.QSlider(Qt.Horizontal)
|
||||||
|
slider.setMinimum(minimum_minutes)
|
||||||
|
slider.setMaximum(maximum_minutes)
|
||||||
|
slider.setValue(initial_minutes)
|
||||||
|
self.spinner = TimeSpinner(minimum_minutes, maximum_minutes, initial_minutes)
|
||||||
|
slider.valueChanged.connect(lambda x: self.spinner.setValue(x))
|
||||||
|
self.spinner.valueChanged.connect(lambda x: slider.setValue(x))
|
||||||
|
|
||||||
|
self.addWidget(slider, 1, 0)
|
||||||
|
self.addWidget(self.spinner, 1, 1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self) -> timedelta:
|
||||||
|
return timedelta(minutes=self.spinner.value())
|
||||||
|
|
||||||
|
|
||||||
|
class TimeSpinner(QtWidgets.QSpinBox):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
minimum: Optional[int] = None,
|
||||||
|
maximum: Optional[int] = None,
|
||||||
|
initial: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if minimum is not None:
|
||||||
|
self.setMinimum(minimum)
|
||||||
|
if maximum is not None:
|
||||||
|
self.setMaximum(maximum)
|
||||||
|
if initial is not None:
|
||||||
|
self.setValue(initial)
|
||||||
|
|
||||||
|
def textFromValue(self, val: int) -> str:
|
||||||
|
return f"{val} minutes"
|
||||||
|
|
||||||
|
|
||||||
|
class CurrencySpinner(QtWidgets.QSpinBox):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
minimum: Optional[int] = None,
|
||||||
|
maximum: Optional[int] = None,
|
||||||
|
initial: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if minimum is not None:
|
||||||
|
self.setMinimum(minimum)
|
||||||
|
if maximum is not None:
|
||||||
|
self.setMaximum(maximum)
|
||||||
|
if initial is not None:
|
||||||
|
self.setValue(initial)
|
||||||
|
|
||||||
|
def textFromValue(self, val: int) -> str:
|
||||||
|
return f"${val}"
|
||||||
|
|||||||
@ -7,12 +7,13 @@ from PySide2 import QtGui, QtWidgets
|
|||||||
from PySide2.QtCore import QItemSelectionModel, QPoint, Qt, QDate
|
from PySide2.QtCore import QItemSelectionModel, QPoint, Qt, QDate
|
||||||
from PySide2.QtWidgets import QVBoxLayout, QTextEdit, QLabel
|
from PySide2.QtWidgets import QVBoxLayout, QTextEdit, QLabel
|
||||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from game.settings import Settings
|
from game.settings import Settings
|
||||||
from game.theater.start_generator import GameGenerator, GeneratorSettings
|
from game.theater.start_generator import GameGenerator, GeneratorSettings
|
||||||
from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar
|
from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar
|
||||||
from qt_ui.widgets.spinsliders import TenthsSpinSlider
|
from qt_ui.widgets.spinsliders import TenthsSpinSlider, TimeInputs, CurrencySpinner
|
||||||
from qt_ui.windows.newgame.QCampaignList import (
|
from qt_ui.windows.newgame.QCampaignList import (
|
||||||
Campaign,
|
Campaign,
|
||||||
QCampaignList,
|
QCampaignList,
|
||||||
@ -33,8 +34,8 @@ jinja_env = Environment(
|
|||||||
lstrip_blocks=True,
|
lstrip_blocks=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_BUDGET = 2000
|
DEFAULT_BUDGET = 2000
|
||||||
|
DEFAULT_MISSION_LENGTH: timedelta = timedelta(minutes=90)
|
||||||
|
|
||||||
|
|
||||||
class NewGameWizard(QtWidgets.QWizard):
|
class NewGameWizard(QtWidgets.QWizard):
|
||||||
@ -85,6 +86,9 @@ class NewGameWizard(QtWidgets.QWizard):
|
|||||||
automate_front_line_reinforcements=self.field(
|
automate_front_line_reinforcements=self.field(
|
||||||
"automate_front_line_purchases"
|
"automate_front_line_purchases"
|
||||||
),
|
),
|
||||||
|
desired_player_mission_duration=timedelta(
|
||||||
|
minutes=self.field("desired_player_mission_duration")
|
||||||
|
),
|
||||||
automate_aircraft_reinforcements=self.field("automate_aircraft_purchases"),
|
automate_aircraft_reinforcements=self.field("automate_aircraft_purchases"),
|
||||||
supercarrier=self.field("supercarrier"),
|
supercarrier=self.field("supercarrier"),
|
||||||
enable_new_ground_unit_recruitment=self.field(
|
enable_new_ground_unit_recruitment=self.field(
|
||||||
@ -428,26 +432,6 @@ class TheaterConfiguration(QtWidgets.QWizardPage):
|
|||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
|
||||||
class CurrencySpinner(QtWidgets.QSpinBox):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
minimum: Optional[int] = None,
|
|
||||||
maximum: Optional[int] = None,
|
|
||||||
initial: Optional[int] = None,
|
|
||||||
) -> None:
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
if minimum is not None:
|
|
||||||
self.setMinimum(minimum)
|
|
||||||
if maximum is not None:
|
|
||||||
self.setMaximum(maximum)
|
|
||||||
if initial is not None:
|
|
||||||
self.setValue(initial)
|
|
||||||
|
|
||||||
def textFromValue(self, val: int) -> str:
|
|
||||||
return f"${val}"
|
|
||||||
|
|
||||||
|
|
||||||
class BudgetInputs(QtWidgets.QGridLayout):
|
class BudgetInputs(QtWidgets.QGridLayout):
|
||||||
def __init__(self, label: str) -> None:
|
def __init__(self, label: str) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -565,6 +549,12 @@ class GeneratorOptions(QtWidgets.QWizardPage):
|
|||||||
self.registerField("no_player_navy", no_player_navy)
|
self.registerField("no_player_navy", no_player_navy)
|
||||||
no_enemy_navy = QtWidgets.QCheckBox()
|
no_enemy_navy = QtWidgets.QCheckBox()
|
||||||
self.registerField("no_enemy_navy", no_enemy_navy)
|
self.registerField("no_enemy_navy", no_enemy_navy)
|
||||||
|
desired_player_mission_duration = TimeInputs(
|
||||||
|
"Desired mission duration", DEFAULT_MISSION_LENGTH
|
||||||
|
)
|
||||||
|
self.registerField(
|
||||||
|
"desired_player_mission_duration", desired_player_mission_duration.spinner
|
||||||
|
)
|
||||||
|
|
||||||
generatorLayout = QtWidgets.QGridLayout()
|
generatorLayout = QtWidgets.QGridLayout()
|
||||||
generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0)
|
generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0)
|
||||||
@ -577,6 +567,7 @@ class GeneratorOptions(QtWidgets.QWizardPage):
|
|||||||
generatorLayout.addWidget(no_player_navy, 4, 1)
|
generatorLayout.addWidget(no_player_navy, 4, 1)
|
||||||
generatorLayout.addWidget(QtWidgets.QLabel("No Enemy Navy"), 5, 0)
|
generatorLayout.addWidget(QtWidgets.QLabel("No Enemy Navy"), 5, 0)
|
||||||
generatorLayout.addWidget(no_enemy_navy, 5, 1)
|
generatorLayout.addWidget(no_enemy_navy, 5, 1)
|
||||||
|
generatorLayout.addLayout(desired_player_mission_duration, 6, 0)
|
||||||
generatorSettingsGroup.setLayout(generatorLayout)
|
generatorSettingsGroup.setLayout(generatorLayout)
|
||||||
|
|
||||||
mlayout = QVBoxLayout()
|
mlayout = QVBoxLayout()
|
||||||
|
|||||||
@ -26,7 +26,7 @@ from game.game import Game
|
|||||||
from game.infos.information import Information
|
from game.infos.information import Information
|
||||||
from game.settings import Settings
|
from game.settings import Settings
|
||||||
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
|
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
|
||||||
from qt_ui.widgets.spinsliders import TenthsSpinSlider
|
from qt_ui.widgets.spinsliders import TenthsSpinSlider, TimeInputs
|
||||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||||
from qt_ui.windows.finances.QFinancesMenu import QHorizontalSeparationLine
|
from qt_ui.windows.finances.QFinancesMenu import QHorizontalSeparationLine
|
||||||
from qt_ui.windows.settings.plugins import PluginOptionsPage, PluginsPage
|
from qt_ui.windows.settings.plugins import PluginOptionsPage, PluginsPage
|
||||||
@ -471,6 +471,14 @@ class QSettingsWindow(QDialog):
|
|||||||
"spawned immediately. AI wingmen may begin startup immediately."
|
"spawned immediately. AI wingmen may begin startup immediately."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.desired_player_mission_duration = TimeInputs(
|
||||||
|
"Desired mission duration",
|
||||||
|
self.game.settings.desired_player_mission_duration,
|
||||||
|
)
|
||||||
|
self.desired_player_mission_duration.spinner.valueChanged.connect(
|
||||||
|
self.applySettings
|
||||||
|
)
|
||||||
|
|
||||||
self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0)
|
self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0)
|
||||||
self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight)
|
self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight)
|
||||||
self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0)
|
self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0)
|
||||||
@ -483,6 +491,9 @@ class QSettingsWindow(QDialog):
|
|||||||
)
|
)
|
||||||
self.gameplayLayout.addWidget(dark_kneeboard_label, 2, 0)
|
self.gameplayLayout.addWidget(dark_kneeboard_label, 2, 0)
|
||||||
self.gameplayLayout.addWidget(self.generate_dark_kneeboard, 2, 1, Qt.AlignRight)
|
self.gameplayLayout.addWidget(self.generate_dark_kneeboard, 2, 1, Qt.AlignRight)
|
||||||
|
self.gameplayLayout.addLayout(
|
||||||
|
self.desired_player_mission_duration, 5, 0, Qt.AlignRight
|
||||||
|
)
|
||||||
|
|
||||||
spawn_players_immediately_tooltip = (
|
spawn_players_immediately_tooltip = (
|
||||||
"Always spawns player aircraft immediately, even if their start time is "
|
"Always spawns player aircraft immediately, even if their start time is "
|
||||||
@ -695,6 +706,10 @@ class QSettingsWindow(QDialog):
|
|||||||
self.generate_dark_kneeboard.isChecked()
|
self.generate_dark_kneeboard.isChecked()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.game.settings.desired_player_mission_duration = (
|
||||||
|
self.desired_player_mission_duration.value
|
||||||
|
)
|
||||||
|
|
||||||
self.game.settings.perf_red_alert_state = self.red_alert.isChecked()
|
self.game.settings.perf_red_alert_state = self.red_alert.isChecked()
|
||||||
self.game.settings.perf_smoke_gen = self.smoke.isChecked()
|
self.game.settings.perf_smoke_gen = self.smoke.isChecked()
|
||||||
self.game.settings.perf_smoke_spacing = self.smoke_spacing.value()
|
self.game.settings.perf_smoke_spacing = self.smoke_spacing.value()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user