From d22943d7557efbcdcc62f0609298b59ef3db687c Mon Sep 17 00:00:00 2001 From: David Pierron Date: Mon, 12 Oct 2020 17:27:13 +0200 Subject: [PATCH] added a customizable plugin system - the base LUA functionality has been implemented as a mandatory plugin - the jtacautolase functionality has been implemented as a plugin - added a VEAF framework plugin The plugins have GUI elements in the Settings window. --- .gitignore | 3 +- .gitmodules | 3 + game/operation/operation.py | 98 +++++++----------- game/settings.py | 3 + gen/flights/ai_flight_planner.py | 4 +- plugin/__init__.py | 10 ++ .../base}/dcs_liberation.lua | 0 {resources/scripts => plugin/base}/json.lua | 0 .../scripts => plugin/base}/mist_4_3_74.lua | 0 plugin/base_plugin.py | 41 ++++++++ .../custom}/__plugins.lst.sample | 0 .../jtacautolase}/JTACAutoLase.lua | 0 plugin/jtacautolase_plugin.py | 80 ++++++++++++++ plugin/liberation_plugin.py | 20 ++++ plugin/veaf | 1 + plugin/veaf_plugin.py | 90 ++++++++++++++++ qt_ui/uiconstants.py | 2 + qt_ui/windows/settings/QSettingsWindow.py | 40 ++++++- resources/ui/misc/light/plugins.png | Bin 0 -> 1268 bytes resources/ui/misc/light/pluginsoptions.png | Bin 0 -> 1345 bytes 20 files changed, 330 insertions(+), 65 deletions(-) create mode 100644 plugin/__init__.py rename {resources/scripts => plugin/base}/dcs_liberation.lua (100%) rename {resources/scripts => plugin/base}/json.lua (100%) rename {resources/scripts => plugin/base}/mist_4_3_74.lua (100%) create mode 100644 plugin/base_plugin.py rename {resources/scripts/plugins => plugin/custom}/__plugins.lst.sample (100%) rename {resources/scripts => plugin/jtacautolase}/JTACAutoLase.lua (100%) create mode 100644 plugin/jtacautolase_plugin.py create mode 100644 plugin/liberation_plugin.py create mode 160000 plugin/veaf create mode 100644 plugin/veaf_plugin.py create mode 100644 resources/ui/misc/light/plugins.png create mode 100644 resources/ui/misc/light/pluginsoptions.png diff --git a/.gitignore b/.gitignore index 1bf595f6..f058e01f 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ logs/ qt_ui/logs/liberation.log *.psd -resources/scripts/plugins/* +plugin/custom/__plugins.lst +plugin/custom/*.lua diff --git a/.gitmodules b/.gitmodules index d8db9cf5..e4041d5d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = pydcs url = https://github.com/pydcs/dcs branch = master +[submodule "plugin/veaf"] + path = plugin/veaf + url = https://github.com/VEAF/dcs-liberation-veaf-framework diff --git a/game/operation/operation.py b/game/operation/operation.py index 66eddb78..b537395e 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -31,7 +31,7 @@ from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator from theater import ControlPoint from .. import db from ..debriefing import Debriefing - +from plugin import BasePlugin, INSTALLED_PLUGINS class Operation: attackers_starting_position = None # type: db.StartingPosition @@ -75,6 +75,7 @@ class Operation: self.departure_cp = departure_cp self.to_cp = to_cp self.is_quick = False + self.listOfPluginsScripts = [] def units_of(self, country_name: str) -> List[UnitType]: return [] @@ -133,6 +134,36 @@ class Operation: else: self.defenders_starting_position = None + def injectLuaTrigger(self, luascript, comment = "LUA script"): + trigger = TriggerStart(comment=comment) + trigger.add_action(DoScript(String(luascript))) + self.current_mission.triggerrules.triggers.append(trigger) + + def bypassPluginScript(self, pluginName, scriptFileMnemonic): + self.listOfPluginsScripts.append(scriptFileMnemonic) + + def injectPluginScript(self, pluginName, scriptFile, scriptFileMnemonic): + if not scriptFileMnemonic in self.listOfPluginsScripts: + self.listOfPluginsScripts.append(scriptFileMnemonic) + + if pluginName == None: + pluginName = "custom" + plugin_path = Path("./plugin",pluginName) + + if scriptFile != None: + scriptFile_path = Path(plugin_path, scriptFile) + if scriptFile_path.exists(): + trigger = TriggerStart(comment="Load " + scriptFileMnemonic) + filename = scriptFile_path.resolve() + fileref = self.current_mission.map_resource.add_resource_file(filename) + trigger.add_action(DoScriptFile(fileref)) + self.current_mission.triggerrules.triggers.append(trigger) + else: + logging.error(f"Cannot find script file {scriptFile} for plugin {pluginName}") + + else: + logging.debug(f"Skipping script file {scriptFile} for plugin {pluginName}") + def generate(self): radio_registry = RadioRegistry() tacan_registry = TacanRegistry() @@ -434,67 +465,12 @@ dcsLiberation.TargetPoints = { trigger.add_action(DoScript(String(lua))) self.current_mission.triggerrules.triggers.append(trigger) - # Inject Plugins Lua Scripts - listOfPluginsScripts = [] - plugin_file_path = Path("./resources/scripts/plugins/__plugins.lst") - if plugin_file_path.exists(): - for line in plugin_file_path.read_text().splitlines(): - name = line.strip() - if not name.startswith( '#' ): - trigger = TriggerStart(comment="Load " + name) - listOfPluginsScripts.append(name) - fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/plugins/" + name) - trigger.add_action(DoScriptFile(fileref)) - self.current_mission.triggerrules.triggers.append(trigger) - else: - logging.info( - f"Not loading plugins, {plugin_file_path} does not exist") + # Inject Plugins Lua Scripts and data + self.listOfPluginsScripts = [] - # Inject Mist Script if not done already in the plugins - if not "mist.lua" in listOfPluginsScripts and not "mist_4_3_74.lua" in listOfPluginsScripts: # don't load the script twice - trigger = TriggerStart(comment="Load Mist Lua framework") - fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/mist_4_3_74.lua") - trigger.add_action(DoScriptFile(fileref)) - self.current_mission.triggerrules.triggers.append(trigger) - - # Inject JSON library if not done already in the plugins - if not "json.lua" in listOfPluginsScripts : # don't load the script twice - trigger = TriggerStart(comment="Load JSON Lua library") - fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/json.lua") - trigger.add_action(DoScriptFile(fileref)) - self.current_mission.triggerrules.triggers.append(trigger) - - # Inject Ciribob's JTACAutoLase if not done already in the plugins - if not "JTACAutoLase.lua" in listOfPluginsScripts : # don't load the script twice - trigger = TriggerStart(comment="Load JTACAutoLase.lua script") - fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/JTACAutoLase.lua") - trigger.add_action(DoScriptFile(fileref)) - self.current_mission.triggerrules.triggers.append(trigger) - - # Inject DCS-Liberation script if not done already in the plugins - if not "dcs_liberation.lua" in listOfPluginsScripts : # don't load the script twice - trigger = TriggerStart(comment="Load DCS Liberation script") - fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/dcs_liberation.lua") - trigger.add_action(DoScriptFile(fileref)) - self.current_mission.triggerrules.triggers.append(trigger) - - # add a configuration for JTACAutoLase and start lasing for all JTACs - smoke = "true" - if hasattr(self.game.settings, "jtac_smoke_on"): - if not self.game.settings.jtac_smoke_on: - smoke = "false" - - lua = """ - -- setting and starting JTACs - env.info("DCSLiberation|: setting and starting JTACs") - """ - - for jtac in jtacs: - lua += f"if dcsLiberation.JTACAutoLase then dcsLiberation.JTACAutoLase('{jtac.unit_name}', {jtac.code}, {smoke}, 'vehicle') end\n" - - trigger = TriggerStart(comment="Start JTACs") - trigger.add_action(DoScript(String(lua))) - self.current_mission.triggerrules.triggers.append(trigger) + for plugin in INSTALLED_PLUGINS: + plugin.injectScripts(self) + plugin.injectConfiguration(self) self.assign_channels_to_flights(airgen.flights, airsupportgen.air_support) diff --git a/game/settings.py b/game/settings.py index 4566ad0f..5d0d5c91 100644 --- a/game/settings.py +++ b/game/settings.py @@ -40,4 +40,7 @@ class Settings: self.perf_culling = False self.perf_culling_distance = 100 + # LUA Plugins system + self.plugins = {} + diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 561f359c..03fe6d32 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -205,7 +205,7 @@ class PackageBuilder: airfield, aircraft = assignment flight = Flight(aircraft, plan.num_aircraft, airfield, plan.task) self.package.add_flight(flight) - flight.targetPoint = location + flight.targetPoint = self.package.target return True def build(self) -> Package: @@ -218,7 +218,7 @@ class PackageBuilder: for flight in flights: self.global_inventory.return_from_flight(flight) self.package.remove_flight(flight) - flight.targetPoint = None + flight.targetPoint = None class ObjectiveFinder: """Identifies potential objectives for the mission planner.""" diff --git a/plugin/__init__.py b/plugin/__init__.py new file mode 100644 index 00000000..c3cd6fb2 --- /dev/null +++ b/plugin/__init__.py @@ -0,0 +1,10 @@ +from .base_plugin import BasePlugin +from .veaf_plugin import VeafPlugin +from .jtacautolase_plugin import JtacAutolasePlugin +from .liberation_plugin import LiberationPlugin + +INSTALLED_PLUGINS=[ + VeafPlugin(), + JtacAutolasePlugin(), + LiberationPlugin() + ] \ No newline at end of file diff --git a/resources/scripts/dcs_liberation.lua b/plugin/base/dcs_liberation.lua similarity index 100% rename from resources/scripts/dcs_liberation.lua rename to plugin/base/dcs_liberation.lua diff --git a/resources/scripts/json.lua b/plugin/base/json.lua similarity index 100% rename from resources/scripts/json.lua rename to plugin/base/json.lua diff --git a/resources/scripts/mist_4_3_74.lua b/plugin/base/mist_4_3_74.lua similarity index 100% rename from resources/scripts/mist_4_3_74.lua rename to plugin/base/mist_4_3_74.lua diff --git a/plugin/base_plugin.py b/plugin/base_plugin.py new file mode 100644 index 00000000..c2a850e2 --- /dev/null +++ b/plugin/base_plugin.py @@ -0,0 +1,41 @@ +from PySide2.QtCore import QSize, Qt, QItemSelectionModel, QPoint +from PySide2.QtWidgets import QLabel, QDialog, QGridLayout, QListView, QStackedLayout, QComboBox, QWidget, \ + QAbstractItemView, QPushButton, QGroupBox, QCheckBox, QVBoxLayout, QSpinBox + +class BasePlugin(): + nameInUI:str = "Base plugin" + nameInSettings:str = "plugin.base" + enabledDefaultValue:bool = False + + def __init__(self): + self.uiWidget: QCheckBox = None + self.enabled = self.enabledDefaultValue + self.settings = None + + def setupUI(self, settingsWindow, row:int): + self.settings = settingsWindow.game.settings + + if not self.nameInSettings in self.settings.plugins: + self.settings.plugins[self.nameInSettings] = self.enabledDefaultValue + + self.uiWidget = QCheckBox() + self.uiWidget.setChecked(self.settings.plugins[self.nameInSettings]) + self.uiWidget.toggled.connect(lambda: self.applySetting(settingsWindow)) + + settingsWindow.pluginsGroupLayout.addWidget(QLabel(self.nameInUI), row, 0) + settingsWindow.pluginsGroupLayout.addWidget(self.uiWidget, row, 1, Qt.AlignRight) + + def applySetting(self, settingsWindow): + self.settings.plugins[self.nameInSettings] = self.uiWidget.isChecked() + self.enabled = self.settings.plugins[self.nameInSettings] + + def injectScripts(self, operation): + self.settings = operation.game.settings + return self.isEnabled() + + def injectConfiguration(self, operation): + self.settings = operation.game.settings + return self.isEnabled() + + def isEnabled(self) -> bool: + return self.settings != None and self.settings.plugins[self.nameInSettings] diff --git a/resources/scripts/plugins/__plugins.lst.sample b/plugin/custom/__plugins.lst.sample similarity index 100% rename from resources/scripts/plugins/__plugins.lst.sample rename to plugin/custom/__plugins.lst.sample diff --git a/resources/scripts/JTACAutoLase.lua b/plugin/jtacautolase/JTACAutoLase.lua similarity index 100% rename from resources/scripts/JTACAutoLase.lua rename to plugin/jtacautolase/JTACAutoLase.lua diff --git a/plugin/jtacautolase_plugin.py b/plugin/jtacautolase_plugin.py new file mode 100644 index 00000000..d8becf03 --- /dev/null +++ b/plugin/jtacautolase_plugin.py @@ -0,0 +1,80 @@ +from dcs.triggers import TriggerStart +from PySide2.QtCore import QSize, Qt, QItemSelectionModel, QPoint +from PySide2.QtWidgets import QLabel, QDialog, QGridLayout, QListView, QStackedLayout, QComboBox, QWidget, \ + QAbstractItemView, QPushButton, QGroupBox, QCheckBox, QVBoxLayout, QSpinBox +from .base_plugin import BasePlugin + +class JtacAutolasePlugin(BasePlugin): + nameInUI:str = "JTAC Autolase" + nameInSettings:str = "plugin.jtacAutolase" + enabledDefaultValue:bool = True + + #Allow spawn option + nameInUI_useSmoke:str = "JTACs use smoke" + nameInSettings_useSmoke:str = "plugin.jtacAutolase.useSmoke" + + def setupUI(self, settingsWindow, row:int): + # call the base method to add the plugin selection checkbox + super().setupUI(settingsWindow, row) + + if settingsWindow.pluginsOptionsPageLayout: + self.optionsGroup = QGroupBox(self.nameInUI) + optionsGroupLayout = QGridLayout(); + optionsGroupLayout.setAlignment(Qt.AlignTop) + self.optionsGroup.setLayout(optionsGroupLayout) + settingsWindow.pluginsOptionsPageLayout.addWidget(self.optionsGroup) + + # JTAC use smoke + if not self.nameInSettings_useSmoke in self.settings.plugins: + self.settings.plugins[self.nameInSettings_useSmoke] = True + + self.uiWidget_useSmoke = QCheckBox() + self.uiWidget_useSmoke.setChecked(self.settings.plugins[self.nameInSettings_useSmoke]) + self.uiWidget_useSmoke.toggled.connect(lambda: self.applySetting(settingsWindow)) + + optionsGroupLayout.addWidget(QLabel(self.nameInUI_useSmoke), 0, 0) + optionsGroupLayout.addWidget(self.uiWidget_useSmoke, 0, 1, Qt.AlignRight) + + # disable or enable the UI in the plugins special page + self.enableOptionsGroup() + + def enableOptionsGroup(self): + pluginEnabled = self.uiWidget.isChecked() + self.optionsGroup.setEnabled(pluginEnabled) + + def applySetting(self, settingsWindow): + # call the base method to apply the plugin selection checkbox value + super().applySetting(settingsWindow) + + # save the "use smoke" option + self.settings.plugins[self.nameInSettings_useSmoke] = self.uiWidget_useSmoke.isChecked() + + # disable or enable the UI in the plugins special page + self.enableOptionsGroup() + + def injectScripts(self, operation): + if super().injectScripts(operation): + operation.injectPluginScript("jtacautolase", "JTACAutoLase.lua", "jtacautolase") + + def injectConfiguration(self, operation): + if super().injectConfiguration(operation): + + # add a configuration for JTACAutoLase and start lasing for all JTACs + smoke = "local smoke = false" + if self.settings.plugins[self.nameInSettings_useSmoke]: + smoke = "local smoke = true" + + lua = smoke + """ + + -- setting and starting JTACs + env.info("DCSLiberation|: setting and starting JTACs") + + for _, jtac in pairs(dcsLiberation.JTACs) do + if dcsLiberation.JTACAutoLase then + dcsLiberation.JTACAutoLase(jtac.dcsUnit, jtac.code, smoke, 'vehicle') + end + end + """ + + operation.injectLuaTrigger(lua, "Setting and starting JTACs") + diff --git a/plugin/liberation_plugin.py b/plugin/liberation_plugin.py new file mode 100644 index 00000000..e9a01a39 --- /dev/null +++ b/plugin/liberation_plugin.py @@ -0,0 +1,20 @@ +from .base_plugin import BasePlugin + +class LiberationPlugin(BasePlugin): + nameInUI:str = "Liberation script" + nameInSettings:str = "plugin.liberation" + enabledDefaultValue:bool = True + + def setupUI(self, settingsWindow, row:int): + # Don't setup any UI, this plugin is mandatory + pass + + def injectScripts(self, operation): + if super().injectScripts(operation): + operation.injectPluginScript("base", "mist_4_3_74.lua", "mist") + operation.injectPluginScript("base", "json.lua", "json") + operation.injectPluginScript("base", "dcs_liberation.lua", "liberation") + + def injectConfiguration(self, operation): + if super().injectConfiguration(operation): + pass diff --git a/plugin/veaf b/plugin/veaf new file mode 160000 index 00000000..219cdffe --- /dev/null +++ b/plugin/veaf @@ -0,0 +1 @@ +Subproject commit 219cdffef087660fe448a41e1f187c4856e9d80f diff --git a/plugin/veaf_plugin.py b/plugin/veaf_plugin.py new file mode 100644 index 00000000..2e3b0531 --- /dev/null +++ b/plugin/veaf_plugin.py @@ -0,0 +1,90 @@ +from PySide2.QtCore import QSize, Qt, QItemSelectionModel, QPoint +from PySide2.QtWidgets import QLabel, QDialog, QGridLayout, QListView, QStackedLayout, QComboBox, QWidget, \ + QAbstractItemView, QPushButton, QGroupBox, QCheckBox, QVBoxLayout, QSpinBox +from .base_plugin import BasePlugin + +class VeafPlugin(BasePlugin): + nameInUI:str = "VEAF framework" + nameInSettings:str = "plugin.veaf" + enabledDefaultValue:bool = False + + #Allow spawn option + nameInUI_allowSpawn:str = "Allow units spawn via markers and CTLD (not implemented yet)" + nameInSettings_allowSpawn:str = "plugin.veaf.allowSpawn" + + def setupUI(self, settingsWindow, row:int): + # call the base method to add the plugin selection checkbox + super().setupUI(settingsWindow, row) + + if settingsWindow.pluginsOptionsPageLayout: + self.optionsGroup = QGroupBox(self.nameInUI) + optionsGroupLayout = QGridLayout(); + optionsGroupLayout.setAlignment(Qt.AlignTop) + self.optionsGroup.setLayout(optionsGroupLayout) + settingsWindow.pluginsOptionsPageLayout.addWidget(self.optionsGroup) + + # allow spawn of objects + if not self.nameInSettings_allowSpawn in self.settings.plugins: + self.settings.plugins[self.nameInSettings_allowSpawn] = True + + self.uiWidget_allowSpawn = QCheckBox() + self.uiWidget_allowSpawn.setChecked(self.settings.plugins[self.nameInSettings_allowSpawn]) + self.uiWidget_allowSpawn.setEnabled(False) + self.uiWidget_allowSpawn.toggled.connect(lambda: self.applySetting(settingsWindow)) + + optionsGroupLayout.addWidget(QLabel(self.nameInUI_allowSpawn), 0, 0) + optionsGroupLayout.addWidget(self.uiWidget_allowSpawn, 0, 1, Qt.AlignRight) + + # disable or enable the UI in the plugins special page + self.enableOptionsGroup() + + def enableOptionsGroup(self): + pluginEnabled = self.uiWidget.isChecked() + self.optionsGroup.setEnabled(pluginEnabled) + + def applySetting(self, settingsWindow): + # call the base method to apply the plugin selection checkbox value + super().applySetting(settingsWindow) + + # save the "allow spawn" option + self.settings.plugins[self.nameInSettings_allowSpawn] = self.uiWidget_allowSpawn.isChecked() + + # disable or enable the UI in the plugins special page + self.enableOptionsGroup() + + def injectScripts(self, operation): + if super().injectScripts(operation): + # bypass JTACAutoLase + operation.bypassPluginScript("veaf", "jtacautolase") + + # inject the required scripts + operation.injectPluginScript("veaf", "src\\scripts\\mist.lua", "mist") + operation.injectPluginScript("veaf", "src\\scripts\\Moose.lua", "moose") + operation.injectPluginScript("veaf", "src\\scripts\\CTLD.lua", "ctld") + operation.injectPluginScript("veaf", "src\\scripts\\NIOD.lua", "niod") + operation.injectPluginScript("veaf", "src\\scripts\\WeatherMark.lua", "weathermark") + operation.injectPluginScript("veaf", "src\\scripts\\veaf.lua", "veaf") + operation.injectPluginScript("veaf", "src\\scripts\\dcsUnits.lua", "dcsunits") + operation.injectPluginScript("veaf", "src\\scripts\\veafAssets.lua", "veafassets") + operation.injectPluginScript("veaf", "src\\scripts\\veafCarrierOperations.lua", "veafcarrieroperations") + operation.injectPluginScript("veaf", "src\\scripts\\veafCasMission.lua", "veafcasmission") + operation.injectPluginScript("veaf", "src\\scripts\\veafCombatMission.lua", "veafcombatmission") + operation.injectPluginScript("veaf", "src\\scripts\\veafCombatZone.lua", "veafcombatzone") + operation.injectPluginScript("veaf", "src\\scripts\\veafGrass.lua", "veafgrass") + operation.injectPluginScript("veaf", "src\\scripts\\veafInterpreter.lua", "veafinterpreter") + operation.injectPluginScript("veaf", "src\\scripts\\veafMarkers.lua", "veafmarkers") + operation.injectPluginScript("veaf", "src\\scripts\\veafMove.lua", "veafmove") + operation.injectPluginScript("veaf", "src\\scripts\\veafNamedPoints.lua", "veafnamedpoints") + operation.injectPluginScript("veaf", "src\\scripts\\veafRadio.lua", "veafradio") + operation.injectPluginScript("veaf", "src\\scripts\\veafRemote.lua", "veafremote") + operation.injectPluginScript("veaf", "src\\scripts\\veafSecurity.lua", "veafsecurity") + operation.injectPluginScript("veaf", "src\\scripts\\veafShortcuts.lua", "veafshortcuts") + operation.injectPluginScript("veaf", "src\\scripts\\veafSpawn.lua", "veafspawn") + operation.injectPluginScript("veaf", "src\\scripts\\veafTransportMission.lua", "veaftransportmission") + operation.injectPluginScript("veaf", "src\\scripts\\veafUnits.lua", "veafunits") + + + def injectConfiguration(self, operation): + if super().injectConfiguration(operation): + operation.injectPluginScript("veaf", "src\\config\\missionConfig.lua", "missionconfig") + diff --git a/qt_ui/uiconstants.py b/qt_ui/uiconstants.py index 5c97dc72..5c831c1e 100644 --- a/qt_ui/uiconstants.py +++ b/qt_ui/uiconstants.py @@ -99,6 +99,8 @@ def load_icons(): ICONS["Generator"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/generator.png") ICONS["Missile"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/missile.png") ICONS["Cheat"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/cheat.png") + ICONS["Plugins"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/plugins.png") + ICONS["PluginsOptions"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/pluginsoptions.png") ICONS["TaskCAS"] = QPixmap("./resources/ui/tasks/cas.png") ICONS["TaskCAP"] = QPixmap("./resources/ui/tasks/cap.png") diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index 6ccdb226..82010be9 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -11,7 +11,7 @@ from game.game import Game from game.infos.information import Information from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.finances.QFinancesMenu import QHorizontalSeparationLine - +from plugin import BasePlugin, INSTALLED_PLUGINS class QSettingsWindow(QDialog): @@ -52,10 +52,22 @@ class QSettingsWindow(QDialog): cheat.setEditable(False) cheat.setSelectable(True) + plugins = QStandardItem("LUA Plugins") + plugins.setIcon(CONST.ICONS["Plugins"]) + plugins.setEditable(False) + plugins.setSelectable(True) + + pluginsOptions = QStandardItem("LUA Plugins Options") + pluginsOptions.setIcon(CONST.ICONS["PluginsOptions"]) + pluginsOptions.setEditable(False) + pluginsOptions.setSelectable(True) + self.categoryList.setIconSize(QSize(32, 32)) self.categoryModel.appendRow(difficulty) self.categoryModel.appendRow(generator) self.categoryModel.appendRow(cheat) + self.categoryModel.appendRow(plugins) + self.categoryModel.appendRow(pluginsOptions) self.categoryList.setSelectionBehavior(QAbstractItemView.SelectRows) self.categoryList.setModel(self.categoryModel) @@ -65,10 +77,13 @@ class QSettingsWindow(QDialog): self.initDifficultyLayout() self.initGeneratorLayout() self.initCheatLayout() + self.initPluginsLayout() self.right_layout.addWidget(self.difficultyPage) self.right_layout.addWidget(self.generatorPage) self.right_layout.addWidget(self.cheatPage) + self.right_layout.addWidget(self.pluginsPage) + self.right_layout.addWidget(self.pluginsOptionsPage) self.layout.addWidget(self.categoryList, 0, 0, 1, 1) self.layout.addLayout(self.right_layout, 0, 1, 5, 1) @@ -283,6 +298,29 @@ class QSettingsWindow(QDialog): self.moneyCheatBoxLayout.addWidget(btn, i/2, i%2) self.cheatLayout.addWidget(self.moneyCheatBox, 0, 0) + def initPluginsLayout(self): + self.pluginsOptionsPage = QWidget() + self.pluginsOptionsPageLayout = QVBoxLayout() + self.pluginsOptionsPageLayout.setAlignment(Qt.AlignTop) + self.pluginsOptionsPage.setLayout(self.pluginsOptionsPageLayout) + + self.pluginsPage = QWidget() + self.pluginsPageLayout = QVBoxLayout() + self.pluginsPageLayout.setAlignment(Qt.AlignTop) + self.pluginsPage.setLayout(self.pluginsPageLayout) + + self.pluginsGroup = QGroupBox("Plugins") + self.pluginsGroupLayout = QGridLayout(); + self.pluginsGroupLayout.setAlignment(Qt.AlignTop) + self.pluginsGroup.setLayout(self.pluginsGroupLayout) + + row:int = 0 + for plugin in INSTALLED_PLUGINS: + plugin.setupUI(self, row) + row = row + 1 + + self.pluginsPageLayout.addWidget(self.pluginsGroup) + def cheatLambda(self, amount): return lambda: self.cheatMoney(amount) diff --git a/resources/ui/misc/light/plugins.png b/resources/ui/misc/light/plugins.png new file mode 100644 index 0000000000000000000000000000000000000000..568d9e9581ed36753a5cfc9c0f431eb8c9aee38f GIT binary patch literal 1268 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