From bfc602f22f9fc3cc7b191d91e2a66b90d6e87e05 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Sat, 24 Oct 2020 13:06:37 -0700 Subject: [PATCH] Add cheat option to capture bases. Capturing bases is sometimes really annoying because of the DCS unit AI and our non-optimal ground victory heuristics. Add a cheat option to allow the player to move on without the tedium. --- game/event/event.py | 16 +++----------- game/game.py | 17 ++++++++------- qt_ui/widgets/map/QMapControlPoint.py | 26 +++++++++++++++++++++++ qt_ui/widgets/map/QMapObject.py | 5 +++++ theater/base.py | 3 +++ theater/controlpoint.py | 30 ++++++++++++++++++++++++++- 6 files changed, 76 insertions(+), 21 deletions(-) diff --git a/game/event/event.py b/game/event/event.py index 8f7ac1b8..e6b7d142 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -195,29 +195,19 @@ class Event: if cp.id == id: if cp.captured and new_owner_coalition != coalition: - cp.captured = False + for_player = False info = Information(cp.name + " lost !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn) self.game.informations.append(info) - pname = self.game.enemy_name captured_cps.append(cp) elif not(cp.captured) and new_owner_coalition == coalition: - cp.captured = True + for_player = True info = Information(cp.name + " captured !", "We took control of " + cp.name + "! Great job !", self.game.turn) self.game.informations.append(info) - pname = self.game.player_name captured_cps.append(cp) else: continue - cp.base.aircraft = {} - cp.base.armor = {} - - airbase_def_id = 0 - for g in cp.ground_objects: - g.groups = [] - if g.airbase_group and pname != "": - generate_airbase_defense_group(airbase_def_id, g, pname, self.game, cp) - airbase_def_id = airbase_def_id + 1 + cp.capture(self.game, for_player) for cp in captured_cps: logging.info("Will run redeploy for " + cp.name) diff --git a/game/game.py b/game/game.py index 2b8f7689..e75808a2 100644 --- a/game/game.py +++ b/game/game.py @@ -237,10 +237,10 @@ class Game: if not hasattr(self, "conditions"): self.conditions = self.generate_conditions() - def pass_turn(self, no_action=False): + def pass_turn(self, no_action: bool = False) -> None: logging.info("Pass turn") self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0)) - self.turn = self.turn + 1 + self.turn += 1 for event in self.events: if self.settings.version == "dev": @@ -261,6 +261,14 @@ class Game: if not cp.is_carrier and not cp.is_lha: cp.base.affect_strength(-PLAYER_BASE_STRENGTH_RECOVERY) + self.conditions = self.generate_conditions() + + self.initialize_turn() + + # Autosave progress + persistency.autosave(self) + + def initialize_turn(self) -> None: self.events = [] self._generate_events() @@ -271,8 +279,6 @@ class Game: for cp in self.theater.controlpoints: self.aircraft_inventory.set_from_control_point(cp) - self.conditions = self.generate_conditions() - # Plan flights & combat for next turn self.__culling_points = self.compute_conflicts_position() self.ground_planners = {} @@ -286,9 +292,6 @@ class Game: gplanner.plan_groundwar() self.ground_planners[cp.id] = gplanner - # Autosave progress - persistency.autosave(self) - def _enemy_reinforcement(self): """ Compute and commision reinforcement for enemy bases diff --git a/qt_ui/widgets/map/QMapControlPoint.py b/qt_ui/widgets/map/QMapControlPoint.py index ef9bf5c9..d90a973d 100644 --- a/qt_ui/widgets/map/QMapControlPoint.py +++ b/qt_ui/widgets/map/QMapControlPoint.py @@ -1,6 +1,7 @@ from typing import Optional from PySide2.QtGui import QColor, QPainter +from PySide2.QtWidgets import QAction, QMenu import qt_ui.uiconstants as const from qt_ui.models import GameModel @@ -8,6 +9,7 @@ from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2 from theater import ControlPoint from .QMapObject import QMapObject from ...displayoptions import DisplayOptions +from ...windows.GameUpdateSignal import GameUpdateSignal class QMapControlPoint(QMapObject): @@ -20,6 +22,9 @@ class QMapControlPoint(QMapObject): self.setZValue(1) self.setToolTip(self.control_point.name) self.base_details_dialog: Optional[QBaseMenu2] = None + self.capture_action = QAction( + f"CHEAT: Capture {self.control_point.name}") + self.capture_action.triggered.connect(self.cheat_capture) def paint(self, painter, option, widget=None) -> None: if DisplayOptions.control_points: @@ -64,3 +69,24 @@ class QMapControlPoint(QMapObject): self.game_model ) self.base_details_dialog.show() + + def add_context_menu_actions(self, menu: QMenu) -> None: + if self.control_point.is_fleet: + return + + if self.control_point.captured: + return + + for connected in self.control_point.connected_points: + if connected.captured: + break + else: + return + + menu.addAction(self.capture_action) + + def cheat_capture(self) -> None: + self.control_point.capture(self.game_model.game, for_player=True) + # Reinitialized ground planners and the like. + self.game_model.game.initialize_turn() + GameUpdateSignal.get_instance().updateGame(self.game_model.game) diff --git a/qt_ui/widgets/map/QMapObject.py b/qt_ui/widgets/map/QMapObject.py index c98cce5e..fa28c333 100644 --- a/qt_ui/widgets/map/QMapObject.py +++ b/qt_ui/widgets/map/QMapObject.py @@ -37,6 +37,9 @@ class QMapObject(QGraphicsRectItem): if event.button() == Qt.LeftButton: self.on_click() + def add_context_menu_actions(self, menu: QMenu) -> None: + pass + def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None: menu = QMenu("Menu", self.parent) @@ -48,6 +51,8 @@ class QMapObject(QGraphicsRectItem): new_package_action.triggered.connect(self.open_new_package_dialog) menu.addAction(new_package_action) + self.add_context_menu_actions(menu) + menu.exec_(event.screenPos()) @property diff --git a/theater/base.py b/theater/base.py index 4ca5dec7..47b3580e 100644 --- a/theater/base.py +++ b/theater/base.py @@ -148,6 +148,9 @@ class Base: elif self.strength <= 0: self.strength = BASE_MIN_STRENGTH + def set_strength_to_minimum(self) -> None: + self.strength = BASE_MIN_STRENGTH + def scramble_count(self, multiplier: float, task: Task = None) -> int: if task: count = sum([v for k, v in self.aircraft.items() if db.unit_task(k) == task]) diff --git a/theater/controlpoint.py b/theater/controlpoint.py index 6f520bd1..96f2c060 100644 --- a/theater/controlpoint.py +++ b/theater/controlpoint.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import re -from typing import Dict, List +from typing import Dict, List, TYPE_CHECKING from enum import Enum from dcs.mapping import Point @@ -17,6 +19,9 @@ from .base import Base from .missiontarget import MissionTarget from .theatergroundobject import TheaterGroundObject +if TYPE_CHECKING: + from game import Game + class ControlPointType(Enum): AIRBASE = 0 # An airbase with slots for everything @@ -207,3 +212,26 @@ class ControlPoint(MissionTarget): def is_friendly(self, to_player: bool) -> bool: return self.captured == to_player + + def capture(self, game: Game, for_player: bool) -> None: + if for_player: + self.captured = True + faction_name = game.player_name + else: + self.captured = False + faction_name = game.enemy_name + + self.base.set_strength_to_minimum() + + self.base.aircraft = {} + self.base.armor = {} + + # Handle cyclic dependency. + from .start_generator import generate_airbase_defense_group + airbase_def_id = 0 + for ground_object in self.ground_objects: + ground_object.groups = [] + if ground_object.airbase_group and faction_name != "": + generate_airbase_defense_group(airbase_def_id, ground_object, + faction_name, game, self) + airbase_def_id = airbase_def_id + 1