Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator

This commit is contained in:
MetalStormGhost
2024-05-03 11:19:48 +03:00
151 changed files with 13076 additions and 6698 deletions

View File

@@ -137,7 +137,8 @@ def run_ui(game: Optional[Game], ui_flags: UiFlags) -> None:
# Replace DCS Mission scripting file to allow DCS Retribution to work
try:
liberation_install.replace_mission_scripting_file()
except:
except Exception as e:
logging.error(e)
error_dialog = QtWidgets.QErrorMessage()
error_dialog.setWindowTitle("Wrong DCS installation directory.")
error_dialog.showMessage(
@@ -317,6 +318,7 @@ def create_game(
no_player_navy=False,
no_enemy_navy=False,
tgo_config=campaign.load_ground_forces_config(),
carrier_config=campaign.load_carrier_config(),
),
ModSettings(
a4_skyhawk=False,
@@ -325,6 +327,7 @@ def create_game(
fa_18efg=False,
fa18ef_tanker=False,
f4bc_phantom=False,
f9f_panther=False,
f22_raptor=False,
f84g_thunderjet=False,
f100_supersabre=False,

View File

@@ -141,6 +141,7 @@ class QFlightList(QListView):
)
return
self.package_model.add_flight(clone)
EventStream.put_nowait(GameUpdateEvents().new_flight(clone))
def cancel_or_abort_flight(self, index: QModelIndex) -> None:
self.package_model.cancel_or_abort_flight_at_index(index)
@@ -338,6 +339,10 @@ class QPackageList(QListView):
)
return
self.ato_model.add_package(clone)
events = GameUpdateEvents()
for f in clone.flights:
events.new_flight(f)
EventStream.put_nowait(events)
def delete_package(self, index: QModelIndex) -> None:
self.ato_model.cancel_or_abort_package_at_index(index)

View File

@@ -97,6 +97,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
wpt.description = f"Friendly unit: {target.name}"
else:
wpt.description = f"Enemy unit: {target.name}"
wpt.waypoint_type = FlightWaypointType.TARGET_POINT
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_airbases:

View File

@@ -109,7 +109,7 @@ class QLiberationWindow(QMainWindow):
if last_save_file:
logging.info("Loading last saved game : " + str(last_save_file))
game = persistency.load_game(last_save_file)
self.migrate_game(game, last_save_file)
game = self.migrate_game(game, last_save_file)
self.onGameGenerated(game)
self.updateWindowTitle(last_save_file if game else None)
else:
@@ -342,7 +342,7 @@ class QLiberationWindow(QMainWindow):
)
if file is not None and file[0] != "":
game = persistency.load_game(file[0])
self.migrate_game(game, file[0])
game = self.migrate_game(game, file[0])
GameUpdateSignal.get_instance().game_loaded.emit(game)
self.updateWindowTitle(file[0])
@@ -350,15 +350,23 @@ class QLiberationWindow(QMainWindow):
def migrate_game(self, game, path):
if game:
is_liberation = ".liberation" in path
Migrator(game, is_liberation)
try:
Migrator(game, is_liberation)
return game
except Exception:
self.incompatible_save_popup(path)
else:
relative_path = Path(path)
QMessageBox.critical(
self,
"Incompatible save",
"Incompatible save file detected, please report the issue on GitHub or Discord.\n"
f"Make sure to include the campaign that fails to load, i.e.:\n\n{relative_path}",
)
self.incompatible_save_popup(path)
return None
def incompatible_save_popup(self, path):
relative_path = Path(path)
QMessageBox.critical(
self,
"Incompatible save",
"Incompatible save file detected, please report the issue on GitHub or Discord.\n"
f"Make sure to include the campaign that fails to load, i.e.:\n\n{relative_path}",
)
def saveGame(self):
logging.info("Saving game")

View File

@@ -239,5 +239,7 @@ class QWaitingForMissionResultWindow(QDialog):
self.sim_controller.set_game(self.game)
for _, f in self.game.db.flights.objects.items():
f.state.reinitialize(self.game.conditions.start_time)
for cp in self.game.theater.controlpoints:
cp.release_parking_slots()
GameUpdateSignal.get_instance().updateGame(self.game)
self.close()

View File

@@ -32,6 +32,7 @@ from game.theater.theatergroundobject import (
EwrGroundObject,
SamGroundObject,
VehicleGroupGroundObject,
ShipGroundObject,
)
from qt_ui.uiconstants import EVENT_ICONS
@@ -188,7 +189,8 @@ class QGroundObjectTemplateLayout(QGroupBox):
@property
def affordable(self) -> bool:
return self.cost <= self.game.blue.budget
coalition = self.ground_object.coalition
return self.cost <= coalition.budget or self.game.turn == 0
def add_theater_group(
self, group_name: str, force_group: ForceGroup, groups: list[TgoLayoutUnitGroup]
@@ -226,7 +228,8 @@ class QGroundObjectTemplateLayout(QGroupBox):
self.game.theater.heading_to_conflict_from(self.ground_object.position)
or self.ground_object.heading
)
self.game.blue.budget -= self.cost
coalition = self.ground_object.coalition
coalition.budget -= self.cost if self.game.turn else 0
self.ground_object.groups = []
for group_name, groups in self.layout_model.groups.items():
for group in groups:
@@ -276,13 +279,17 @@ class QGroundObjectBuyMenu(QDialog):
elif isinstance(ground_object, EwrGroundObject):
role = GroupRole.AIR_DEFENSE
tasks.append(GroupTask.EARLY_WARNING_RADAR)
elif isinstance(ground_object, ShipGroundObject):
role = GroupRole.NAVAL
tasks.append(GroupTask.NAVY)
else:
raise NotImplementedError(f"Unhandled TGO type {ground_object.__class__}")
if not tasks:
tasks = role.tasks
for group in game.blue.armed_forces.groups_for_tasks(tasks):
coalition = ground_object.coalition
for group in coalition.armed_forces.groups_for_tasks(tasks):
self.force_group_selector.addItem(group.name, userData=group)
self.force_group_selector.setEnabled(self.force_group_selector.count() > 1)
self.force_group_selector.adjustSize()

View File

@@ -104,15 +104,23 @@ class QGroundObjectMenu(QDialog):
self.buy_replace.clicked.connect(self.buy_group)
self.buy_replace.setProperty("style", "btn-success")
if self.ground_object.purchasable:
if self.ground_object.purchasable or self.game.turn == 0:
if self.total_value > 0:
self.actionLayout.addWidget(self.sell_all_button)
self.actionLayout.addWidget(self.buy_replace)
if self.cp.captured and self.ground_object.purchasable:
if self.show_buy_sell_actions and (
self.ground_object.purchasable or self.game.turn == 0
):
self.mainLayout.addLayout(self.actionLayout)
self.setLayout(self.mainLayout)
@property
def show_buy_sell_actions(self) -> bool:
buysell_allowed = self.game.settings.enable_enemy_buy_sell
buysell_allowed |= self.cp.captured
return buysell_allowed
def doLayout(self):
self.update_total_value()
self.intelBox = QGroupBox("Units :")
@@ -240,7 +248,7 @@ class QGroundObjectMenu(QDialog):
self.actionLayout.addWidget(self.sell_all_button)
self.actionLayout.addWidget(self.buy_replace)
if self.cp.captured and self.ground_object.purchasable:
if self.show_buy_sell_actions and self.ground_object.purchasable:
self.mainLayout.addLayout(self.actionLayout)
except Exception as e:
logging.exception(e)
@@ -276,7 +284,8 @@ class QGroundObjectMenu(QDialog):
def sell_all(self):
self.update_total_value()
self.game.blue.budget += self.total_value
coalition = self.ground_object.coalition
coalition.budget += self.total_value
self.ground_object.groups = []
self.update_game()
@@ -296,7 +305,10 @@ class QGroundObjectMenu(QDialog):
for package in self.game.ato_for(player=False).packages
):
# Replan if the tgo was a target of the redfor
self.game.initialize_turn(events, for_red=True, for_blue=False)
coalition = self.ground_object.coalition
self.game.initialize_turn(
events, for_red=coalition.player, for_blue=not coalition.player
)
EventStream.put_nowait(events)
GameUpdateSignal.get_instance().updateGame(self.game)
# Refresh the dialog

View File

@@ -223,7 +223,7 @@ class QAutoCreateDialog(QDialog):
self.game.settings,
)
now = self.package_model.game_model.sim_controller.current_time_in_sim
package = pff.plan_mission(pm, 1, now, tracer)
package = pff.plan_mission(pm, 1, now, tracer, ignore_range=True)
if package:
package.set_tot_asap(now)
self.package = package

View File

@@ -17,6 +17,7 @@ from game.ato.flight import Flight
from game.ato.flightmember import FlightMember
from game.ato.loadouts import Loadout
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
from qt_ui.widgets.combos.QSquadronLiverySelector import SquadronLiverySelector
from .QLoadoutEditor import QLoadoutEditor
from .ownlasercodeinfo import OwnLaserCodeInfo
from .propertyeditor import PropertyEditor
@@ -141,6 +142,24 @@ class QFlightPayloadTab(QFrame):
)
)
hbox = QHBoxLayout()
self.same_livery_for_all_checkbox = QCheckBox(
"Use same livery for all flight members"
)
self.same_livery_for_all_checkbox.setChecked(
self.flight.use_same_livery_for_all_members
)
self.same_livery_for_all_checkbox.toggled.connect(self.on_same_livery_toggled)
hbox.addWidget(self.same_livery_for_all_checkbox)
self.livery_selector = SquadronLiverySelector(self.flight.squadron)
self.livery_selector.insertItem(0, "Default", None)
self.livery_selector.setCurrentIndex(
self.livery_selector.findData(self.member_selector.selected_member.livery)
)
self.livery_selector.currentIndexChanged.connect(self.on_livery_change)
hbox.addWidget(self.livery_selector)
layout.addLayout(hbox)
scroll_content = QWidget()
scrolling_layout = QVBoxLayout()
scroll_content.setLayout(scrolling_layout)
@@ -212,6 +231,9 @@ class QFlightPayloadTab(QFrame):
self.property_editor.set_flight_member(member)
self.loadout_selector.setCurrentText(member.loadout.name)
self.loadout_selector.setDisabled(member.loadout.is_custom)
self.livery_selector.setCurrentIndex(
self.livery_selector.findData(member.livery)
)
self.payload_editor.set_flight_member(member)
self.weapon_laser_code_selector.set_flight_member(member)
self.own_laser_code_info.set_flight_member(member)
@@ -222,9 +244,13 @@ class QFlightPayloadTab(QFrame):
self.payload_editor.setDisabled(
self.flight.use_same_loadout_for_all_members
)
self.livery_selector.setDisabled(
self.flight.use_same_livery_for_all_members
)
else:
self.loadout_selector.setEnabled(True)
self.payload_editor.setEnabled(True)
self.livery_selector.setEnabled(True)
def loadout_at(self, index: int) -> Loadout:
loadout = self.loadout_selector.itemData(index)
@@ -273,3 +299,20 @@ class QFlightPayloadTab(QFrame):
self.rebind_to_selected_member()
else:
self.flight.roster.use_distinct_loadouts_for_each_member()
def on_same_livery_toggled(self, checked: bool) -> None:
self.flight.use_same_livery_for_all_members = checked
if self.member_selector.value():
self.livery_selector.setDisabled(checked)
if checked:
self.flight.roster.use_same_livery_for_all_members()
if self.member_selector.value():
self.rebind_to_selected_member()
def on_livery_change(self) -> None:
livery = self.livery_selector.currentData()
if self.flight.use_same_livery_for_all_members:
for m in self.flight.roster.members:
m.livery = livery
else:
self.member_selector.selected_member.livery = livery

View File

@@ -86,7 +86,7 @@ class QFlightWaypointList(QTableView):
self.model.setItem(row, 0, QWaypointItem(waypoint, row))
altitude = int(waypoint.alt.feet)
altitude = round(waypoint.alt.feet)
altitude_item = QStandardItem(f"{altitude}")
altitude_item.setEditable(True)
self.model.setItem(row, 1, altitude_item)

View File

@@ -162,9 +162,9 @@ class QFlightWaypointTab(QFrame):
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
self.flight.flight_plan.layout.custom_waypoints.remove(waypoint)
def confirm_degrade(self) -> bool:
def confirm_degrade(self, parent: Optional[QWidget] = None) -> bool:
result = QMessageBox.warning(
self,
parent if parent else self,
"Degrade flight-plan?",
"Deleting the selected waypoint(s) will require degradation to a custom flight-plan. "
"A custom flight-plan will no longer respect the TOTs of the package.<br><br>"
@@ -184,12 +184,6 @@ class QFlightWaypointTab(QFrame):
def on_waypoints_added(self, waypoints: Iterable[FlightWaypoint]) -> None:
if not waypoints:
return
if not self.flight.flight_plan.is_custom:
confirmed = self.confirm_degrade()
if not confirmed:
return
self.degrade_to_custom_flight_plan()
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
self.flight.flight_plan.layout.custom_waypoints.extend(waypoints)
self.add_rows(len(list(waypoints)))

View File

@@ -84,9 +84,11 @@ class NewGameWizard(QtWidgets.QWizard):
no_player_navy=self.field("no_player_navy"),
no_enemy_navy=self.field("no_enemy_navy"),
tgo_config=campaign.load_ground_forces_config(),
carrier_config=campaign.load_carrier_config(),
squadrons_start_full=self.field("squadrons_start_full"),
)
mod_settings = ModSettings(
f9f_panther=self.field("f9f_panther"),
a4_skyhawk=self.field("a4_skyhawk"),
a6a_intruder=self.field("a6a_intruder"),
a7e_corsair2=self.field("a7e_corsair2"),
@@ -105,6 +107,7 @@ class NewGameWizard(QtWidgets.QWizard):
irondome=self.field("irondome"),
uh_60l=self.field("uh_60l"),
jas39_gripen=self.field("jas39_gripen"),
super_etendard=self.field("super_etendard"),
su30_flanker_h=self.field("su30_flanker_h"),
su57_felon=self.field("su57_felon"),
ov10a_bronco=self.field("ov10a_bronco"),

View File

@@ -100,6 +100,8 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.registerField("uh_60l", self.uh_60l)
self.f4bc_phantom = QtWidgets.QCheckBox()
self.registerField("f4bc_phantom", self.f4bc_phantom)
self.f9f_panther = QtWidgets.QCheckBox()
self.registerField("f9f_panther", self.f9f_panther)
self.f15d_baz = QtWidgets.QCheckBox()
self.registerField("f15d_baz", self.f15d_baz)
self.f_15_idf = QtWidgets.QCheckBox()
@@ -122,6 +124,8 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.registerField("f105_thunderchief", self.f105_thunderchief)
self.jas39_gripen = QtWidgets.QCheckBox()
self.registerField("jas39_gripen", self.jas39_gripen)
self.super_etendard = QtWidgets.QCheckBox()
self.registerField("super_etendard", self.super_etendard)
self.su30_flanker_h = QtWidgets.QCheckBox()
self.registerField("su30_flanker_h", self.su30_flanker_h)
self.su57_felon = QtWidgets.QCheckBox()
@@ -150,6 +154,7 @@ class GeneratorOptions(QtWidgets.QWizardPage):
modLayout_row = 1
mod_pairs = [
("F9F Panther (v2.8.7.101)", self.f9f_panther),
("A-4E Skyhawk (v2.2.0)", self.a4_skyhawk),
("A-6A Intruder (v2.7.5.01)", self.a6a_intruder),
("A-7E Corsair II", self.a7e_corsair2),
@@ -158,7 +163,7 @@ class GeneratorOptions(QtWidgets.QWizardPage):
("F-15D Baz (v1.0)", self.f15d_baz),
("F-15I Ra'am (v1.0 by IDF Mods Project)", self.f_15_idf),
("F-16I Sufa & F-16D (v3.6 by IDF Mods Project)", self.f_16_idf),
("F/A-18E/F/G Super Hornet (version 2.1)", self.fa_18efg),
("F/A-18E/F/G Super Hornet (version 2.2.5)", self.fa_18efg),
("F/A-18E/F Super Hornet AI Tanker (version 1.4)", self.fa18ef_tanker),
("F-22A Raptor", self.f22_raptor),
("F-84G Thunderjet (v2.5.7.01)", self.f84g_thunderjet),
@@ -170,6 +175,7 @@ class GeneratorOptions(QtWidgets.QWizardPage):
("Swedish Military Assets pack (1.10)", self.swedishmilitaryassetspack),
("JAS 39 Gripen (v1.8.5-beta)", self.jas39_gripen),
("OV-10A Bronco", self.ov10a_bronco),
("Super Étendard (v2.5.5)", self.super_etendard),
("Su-30 Flanker-H (V2.7.3 beta)", self.su30_flanker_h),
("Su-57 Felon (build-04)", self.su57_felon),
("UH-60L Black Hawk (v1.3.1)", self.uh_60l),
@@ -212,6 +218,7 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.no_enemy_navy.setChecked(s.get("no_enemy_navy", False))
self.squadrons_start_full.setChecked(s.get("squadron_start_full", False))
self.f9f_panther.setChecked(s.get("f9f_panther", False))
self.a4_skyhawk.setChecked(s.get("a4_skyhawk", False))
self.a6a_intruder.setChecked(s.get("a6a_intruder", False))
self.a7e_corsair2.setChecked(s.get("a7e_corsair2", False))
@@ -228,6 +235,7 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.f104_starfighter.setChecked(s.get("f104_starfighter", False))
self.f105_thunderchief.setChecked(s.get("f105_thunderchief", False))
self.jas39_gripen.setChecked(s.get("jas39_gripen", False))
self.super_etendard.setChecked(s.get("super_etendard", False))
self.su30_flanker_h.setChecked(s.get("su30_flanker_h", False))
self.su57_felon.setChecked(s.get("su57_felon", False))
self.ov10a_bronco.setChecked(s.get("ov10a_bronco", False))

View File

@@ -113,6 +113,15 @@ class CheatSettingsBox(QGroupBox):
)
self.main_layout.addLayout(self.air_wing_cheat)
# Buy/Sell actions for OPFOR
self.opfor_buysell_checkbox = QCheckBox()
self.opfor_buysell_checkbox.setChecked(sc.settings.enable_enemy_buy_sell)
self.opfor_buysell_checkbox.toggled.connect(apply_settings)
self.redfor_buysell_cheat = QLabeledWidget(
"Enable OPFOR Buy/Sell actions Cheat:", self.opfor_buysell_checkbox
)
self.main_layout.addLayout(self.redfor_buysell_cheat)
@property
def show_red_ato(self) -> bool:
return self.red_ato_checkbox.isChecked()
@@ -137,6 +146,10 @@ class CheatSettingsBox(QGroupBox):
def enable_air_wing_cheats(self) -> bool:
return self.air_wing_adjustments_checkbox.isChecked()
@property
def enable_redfor_buysell(self) -> bool:
return self.opfor_buysell_checkbox.isChecked()
class AutoSettingsLayout(QGridLayout):
def __init__(
@@ -495,6 +508,7 @@ class QSettingsWidget(QtWidgets.QWizardPage, SettingsContainer):
self.settings.enable_air_wing_adjustments = (
self.cheat_options.enable_air_wing_cheats
)
self.settings.enable_enemy_buy_sell = self.cheat_options.enable_redfor_buysell
if self.game:
events = GameUpdateEvents()
@@ -521,6 +535,15 @@ class QSettingsWidget(QtWidgets.QWizardPage, SettingsContainer):
self.cheat_options.transfer_cheat_checkbox.setChecked(
self.settings.enable_transfer_cheat
)
self.cheat_options.base_runway_state_cheat_checkbox.setChecked(
self.settings.enable_runway_state_cheat
)
self.cheat_options.air_wing_adjustments_checkbox.setChecked(
self.settings.enable_air_wing_adjustments
)
self.cheat_options.opfor_buysell_checkbox.setChecked(
self.settings.enable_enemy_buy_sell
)
self.pluginsPage.update_from_settings()
self.pluginsOptionsPage.update_from_settings()