From 1f12546ff4b436bdcbcc06977a3c13bab732d11c Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Tue, 17 Nov 2020 18:56:35 -0800 Subject: [PATCH] Add command line option to generate a new game. Saves us a ton of clicks while developing the campaign generator. --- game/settings.py | 78 +++++++++++----------- qt_ui/main.py | 86 +++++++++++++++++++++---- qt_ui/models.py | 12 ++-- qt_ui/windows/QLiberationWindow.py | 11 ++-- qt_ui/windows/newgame/QNewGameWizard.py | 15 +++-- 5 files changed, 136 insertions(+), 66 deletions(-) diff --git a/game/settings.py b/game/settings.py index 764e5ff5..c292da5a 100644 --- a/game/settings.py +++ b/game/settings.py @@ -1,52 +1,52 @@ +from dataclasses import dataclass, field from typing import Dict +@dataclass class Settings: + # Generator settings + inverted: bool = False + do_not_generate_carrier: bool = False # TODO : implement + do_not_generate_lha: bool = False # TODO : implement + do_not_generate_player_navy: bool = True # TODO : implement + do_not_generate_enemy_navy: bool = True # TODO : implement - def __init__(self): - # Generator settings - self.inverted = False - self.do_not_generate_carrier = False # TODO : implement - self.do_not_generate_lha = False # TODO : implement - self.do_not_generate_player_navy = True # TODO : implement - self.do_not_generate_enemy_navy = True # TODO : implement + # Difficulty settings + player_skill: str = "Good" + enemy_skill: str = "Average" + enemy_vehicle_skill: str = "Average" + map_coalition_visibility: str = "All Units" + labels: str = "Full" + only_player_takeoff: bool = True # Legacy parameter do not use + night_disabled: bool = False + external_views_allowed: bool = True + supercarrier: bool = False + multiplier: bool = 1 + generate_marks: bool = True + sams: bool = True # Legacy parameter do not use + cold_start: bool = False # Legacy parameter do not use + version: bool = None - # Difficulty settings - self.player_skill = "Good" - self.enemy_skill = "Average" - self.enemy_vehicle_skill = "Average" - self.map_coalition_visibility = "All Units" - self.labels = "Full" - self.only_player_takeoff = True # Legacy parameter do not use - self.night_disabled = False - self.external_views_allowed = True - self.supercarrier = False - self.multiplier = 1 - self.generate_marks = True - self.sams = True # Legacy parameter do not use - self.cold_start = False # Legacy parameter do not use - self.version = None + # Performance oriented + perf_red_alert_state: bool = True + perf_smoke_gen: bool = True + 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 oriented - self.perf_red_alert_state = True - self.perf_smoke_gen = True - self.perf_artillery = True - self.perf_moving_units = True - self.perf_infantry = True - self.perf_ai_parking_start = True - self.perf_destroyed_units = True + # Performance culling + perf_culling: bool = False + perf_culling_distance: int = 100 - # Performance culling - self.perf_culling = False - self.perf_culling_distance = 100 + # LUA Plugins system + plugins: Dict[str, bool] = field(default_factory=dict) - # LUA Plugins system - self.plugins: Dict[str, bool] = {} + # Cheating + show_red_ato: bool = False - # Cheating - self.show_red_ato = False - - self.never_delay_player_flights = False + never_delay_player_flights: bool = False @staticmethod def plugin_settings_key(identifier: str) -> str: diff --git a/qt_ui/main.py b/qt_ui/main.py index de179f6b..8d8ef39a 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -1,13 +1,19 @@ +import argparse import logging import os import sys +from datetime import datetime +from pathlib import Path +from typing import Optional import dcs from PySide2 import QtWidgets from PySide2.QtGui import QPixmap from PySide2.QtWidgets import QApplication, QSplashScreen -from game import db, persistency, VERSION +from game import Game, db, persistency, VERSION +from game.settings import Settings +from game.theater.start_generator import GameGenerator from qt_ui import ( liberation_install, liberation_theme, @@ -16,36 +22,30 @@ from qt_ui import ( ) from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.QLiberationWindow import QLiberationWindow +from qt_ui.windows.newgame.QCampaignList import Campaign from qt_ui.windows.preferences.QLiberationFirstStartWindow import \ QLiberationFirstStartWindow -# Logging setup -logging_config.init_logging(VERSION) - -if __name__ == "__main__": - # Load eagerly to catch errors early. - db.FACTIONS.initialize() +def run_ui(game: Optional[Game] = None) -> None: os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # Potential fix for 4K screens app = QApplication(sys.argv) # init the theme and load the stylesheet based on the theme index liberation_theme.init() - css = "" with open("./resources/stylesheets/"+liberation_theme.get_theme_css_file()) as stylesheet: app.setStyleSheet(stylesheet.read()) # Inject custom payload in pydcs framework custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads") if os.path.exists(custom_payloads): - dcs.planes.FlyingType.payload_dirs.append(custom_payloads) + dcs.unittype.FlyingType.payload_dirs.append(custom_payloads) else: # For release version the path is different. custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\customized_payloads") if os.path.exists(custom_payloads): - dcs.planes.FlyingType.payload_dirs.append(custom_payloads) - + dcs.unittype.FlyingType.payload_dirs.append(custom_payloads) first_start = liberation_install.init() if first_start: @@ -79,7 +79,7 @@ if __name__ == "__main__": GameUpdateSignal() # Start window - window = QLiberationWindow() + window = QLiberationWindow(game) window.showMaximized() splash.finish(window) qt_execution_code = app.exec_() @@ -91,3 +91,65 @@ if __name__ == "__main__": logging.info("QT process exited with code : " + str(qt_execution_code)) +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="subcommand") + + def path_arg(arg: str) -> Path: + path = Path(arg) + if not path.exists(): + raise argparse.ArgumentTypeError("path does not exist") + return path + + new_game = subparsers.add_parser("new-game") + + new_game.add_argument( + "campaign", type=path_arg, + help="Path to the campaign to start." + ) + + new_game.add_argument( + "--blue", default="USA 2005", help="Name of the blue faction." + ) + + new_game.add_argument( + "--red", default="Russia 1990", help="Name of the red faction." + ) + + new_game.add_argument( + "--supercarrier", action="store_true", + help="Use the supercarrier module." + ) + + return parser.parse_args() + + +def create_game(campaign_path: Path, blue: str, red: str, + supercarrier: bool) -> Game: + campaign = Campaign.from_json(campaign_path) + generator = GameGenerator(blue, red, campaign.theater, + Settings(supercarrier=supercarrier), + start_date=datetime.today(), + starting_budget=650, + multiplier=1, midgame=False) + return generator.generate() + + +def main(): + logging_config.init_logging(VERSION) + + # Load eagerly to catch errors early. + db.FACTIONS.initialize() + + game: Optional[Game] = None + + args = parse_args() + if args.subcommand == "new-game": + game = create_game(args.campaign, args.blue, args.red, + args.supercarrier) + + run_ui(game) + + +if __name__ == "__main__": + main() diff --git a/qt_ui/models.py b/qt_ui/models.py index ed5086e0..45a25f50 100644 --- a/qt_ui/models.py +++ b/qt_ui/models.py @@ -277,10 +277,14 @@ class GameModel: This isn't a real Qt data model, but simplifies management of the game and its ATO objects. """ - def __init__(self) -> None: - self.game: Optional[Game] = None - self.ato_model = AtoModel(self.game, AirTaskingOrder()) - self.red_ato_model = AtoModel(self.game, AirTaskingOrder()) + def __init__(self, game: Optional[Game]) -> None: + self.game: Optional[Game] = game + if self.game is None: + self.ato_model = AtoModel(self.game, AirTaskingOrder()) + self.red_ato_model = AtoModel(self.game, AirTaskingOrder()) + else: + self.ato_model = AtoModel(self.game, self.game.blue_ato) + self.red_ato_model = AtoModel(self.game, self.game.red_ato) def set(self, game: Optional[Game]) -> None: """Updates the managed Game object. diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index 67dba6f8..ca80bf72 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -35,11 +35,11 @@ from qt_ui.windows.preferences.QLiberationPreferencesWindow import \ class QLiberationWindow(QMainWindow): - def __init__(self): + def __init__(self, game: Optional[Game]) -> None: super(QLiberationWindow, self).__init__() - self.game: Optional[Game] = None - self.game_model = GameModel() + self.game = game + self.game_model = GameModel(game) Dialog.set_game(self.game_model) self.ato_panel = QAirTaskingOrderPanel(self.game_model) self.info_panel = QInfoPanel(self.game) @@ -60,7 +60,10 @@ class QLiberationWindow(QMainWindow): self.setGeometry(0, 0, screen.width(), screen.height()) self.setWindowState(Qt.WindowMaximized) - self.onGameGenerated(persistency.restore_game()) + if self.game is None: + self.onGameGenerated(persistency.restore_game()) + else: + self.onGameGenerated(self.game) def initUi(self): hbox = QSplitter(Qt.Horizontal) diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index eaaad876..4d102598 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -74,13 +74,14 @@ class NewGameWizard(QtWidgets.QWizard): player_name = blueFaction enemy_name = redFaction - settings = Settings() - settings.inverted = invertMap - settings.supercarrier = supercarrier - settings.do_not_generate_carrier = no_carrier - settings.do_not_generate_lha = no_lha - settings.do_not_generate_player_navy = no_player_navy - settings.do_not_generate_enemy_navy = no_enemy_navy + settings = Settings( + inverted=invertMap, + supercarrier=supercarrier, + do_not_generate_carrier=no_carrier, + do_not_generate_lha=no_lha, + do_not_generate_player_navy=no_player_navy, + do_not_generate_enemy_navy=no_enemy_navy + ) generator = GameGenerator(player_name, enemy_name, conflictTheater, settings, timePeriod, starting_money,