mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Remove the old map.
This commit is contained in:
parent
acd3e87996
commit
30f6220c3e
@ -23,7 +23,7 @@ Saves from 2.5 are not compatible with 3.0.
|
||||
* **[Flight Planner]** Automatic ATO generation for the player's coalition can now be disabled in the settings.
|
||||
* **[Payloads]** AI flights for most air to ground mission types (CAS excluded) will have their guns emptied to prevent strafing fully armed and operational battle stations. Gun-reliant airframes like A-10s and warbirds will keep their bullets.
|
||||
* **[Kneeboard]** ATC table overflow alleviated by wrapping long airfield names and splitting ATC frequency and channel into separate rows.
|
||||
* **[UI]** Added new web based map UI. This is mostly functional but many of the old display options are a WIP. Revert to the old map with --old-map.
|
||||
* **[UI]** Overhauled the map implementation. Now uses satellite imagery instead of low res map images. Display options have moved from the toolbar to panels in the map.
|
||||
* **[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]** DCS loadouts are now selectable in the loadout setup menu.
|
||||
* **[UI]** Added global aircraft inventory view under Air Wing dialog.
|
||||
|
||||
@ -1,128 +0,0 @@
|
||||
"""Visibility options for the game map."""
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Iterator, Optional, Union
|
||||
|
||||
|
||||
@dataclass
|
||||
class DisplayRule:
|
||||
name: str
|
||||
_value: bool
|
||||
debug_only: bool = field(default=False)
|
||||
|
||||
@property
|
||||
def menu_text(self) -> str:
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def value(self) -> bool:
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, value: bool) -> None:
|
||||
from qt_ui.widgets.map.QLiberationMap import QLiberationMap
|
||||
|
||||
self._value = value
|
||||
if QLiberationMap.instance is not None:
|
||||
QLiberationMap.instance.reload_scene()
|
||||
QLiberationMap.instance.update()
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return self.value
|
||||
|
||||
|
||||
class DisplayGroup:
|
||||
def __init__(self, name: Optional[str], debug_only: bool = False) -> None:
|
||||
self.name = name
|
||||
self.debug_only = debug_only
|
||||
|
||||
def __iter__(self) -> Iterator[DisplayRule]:
|
||||
# Python 3.6 enforces that __dict__ is order preserving by default.
|
||||
for value in self.__dict__.values():
|
||||
if isinstance(value, DisplayRule):
|
||||
yield value
|
||||
|
||||
|
||||
class FlightPathOptions(DisplayGroup):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Flight Paths")
|
||||
self.hide = DisplayRule("Hide Flight Paths", False)
|
||||
self.only_selected = DisplayRule("Show Selected Flight Path", False)
|
||||
self.all = DisplayRule("Show All Flight Paths", True)
|
||||
|
||||
|
||||
class ThreatZoneOptions(DisplayGroup):
|
||||
def __init__(self, coalition_name: str) -> None:
|
||||
super().__init__(f"{coalition_name} Threat Zones")
|
||||
self.none = DisplayRule(f"Hide {coalition_name.lower()} threat zones", True)
|
||||
self.all = DisplayRule(
|
||||
f"Show full {coalition_name.lower()} threat zones", False
|
||||
)
|
||||
self.aircraft = DisplayRule(
|
||||
f"Show {coalition_name.lower()} aircraft threat tones", False
|
||||
)
|
||||
self.air_defenses = DisplayRule(
|
||||
f"Show {coalition_name.lower()} air defenses threat zones", False
|
||||
)
|
||||
|
||||
|
||||
class NavMeshOptions(DisplayGroup):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Navmeshes", debug_only=True)
|
||||
self.hide = DisplayRule("DEBUG Hide Navmeshes", True)
|
||||
self.blue_navmesh = DisplayRule("DEBUG Show blue navmesh", False)
|
||||
self.red_navmesh = DisplayRule("DEBUG Show red navmesh", False)
|
||||
|
||||
|
||||
class PathDebugFactionOptions(DisplayGroup):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Faction for path debugging", debug_only=True)
|
||||
self.blue = DisplayRule("Debug blue paths", True)
|
||||
self.red = DisplayRule("Debug red paths", False)
|
||||
|
||||
|
||||
class PathDebugOptions(DisplayGroup):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Shortest paths", debug_only=True)
|
||||
self.hide = DisplayRule("DEBUG Hide paths", True)
|
||||
self.shortest_path = DisplayRule("DEBUG Show shortest path", False)
|
||||
self.barcap = DisplayRule("DEBUG Show BARCAP plan", False)
|
||||
self.cas = DisplayRule("DEBUG Show CAS plan", False)
|
||||
self.sweep = DisplayRule("DEBUG Show fighter sweep plan", False)
|
||||
self.strike = DisplayRule("DEBUG Show strike plan", False)
|
||||
self.tarcap = DisplayRule("DEBUG Show TARCAP plan", False)
|
||||
|
||||
|
||||
class DisplayOptions:
|
||||
ground_objects = DisplayRule("Ground Objects", True)
|
||||
control_points = DisplayRule("Control Points", True)
|
||||
lines = DisplayRule("Lines", True)
|
||||
sam_ranges = DisplayRule("Ally SAM Threat Range", False)
|
||||
enemy_sam_ranges = DisplayRule("Enemy SAM Threat Range", True)
|
||||
detection_range = DisplayRule("SAM Detection Range", False)
|
||||
map_poly = DisplayRule("Map Polygon Debug Mode", False)
|
||||
waypoint_info = DisplayRule("Waypoint Information", True)
|
||||
culling = DisplayRule("Display Culling Zones", False)
|
||||
actual_frontline_pos = DisplayRule("Display Actual Frontline Location", False)
|
||||
patrol_engagement_range = DisplayRule(
|
||||
"Display selected patrol engagement range", True
|
||||
)
|
||||
flight_paths = FlightPathOptions()
|
||||
blue_threat_zones = ThreatZoneOptions("Blue")
|
||||
red_threat_zones = ThreatZoneOptions("Red")
|
||||
navmeshes = NavMeshOptions()
|
||||
path_debug_faction = PathDebugFactionOptions()
|
||||
path_debug = PathDebugOptions()
|
||||
|
||||
@classmethod
|
||||
def menu_items(cls) -> Iterator[Union[DisplayGroup, DisplayRule]]:
|
||||
debug = False # Set to True to enable debug options.
|
||||
# Python 3.6 enforces that __dict__ is order preserving by default.
|
||||
for value in cls.__dict__.values():
|
||||
if isinstance(value, DisplayRule):
|
||||
if value.debug_only and not debug:
|
||||
continue
|
||||
yield value
|
||||
elif isinstance(value, DisplayGroup):
|
||||
if value.debug_only and not debug:
|
||||
continue
|
||||
yield value
|
||||
@ -57,7 +57,7 @@ def inject_custom_payloads(user_path: Path) -> None:
|
||||
PayloadDirectories.set_preferred(user_path / "MissionEditor" / "UnitPayloads")
|
||||
|
||||
|
||||
def run_ui(game: Optional[Game], new_map: bool) -> None:
|
||||
def run_ui(game: Optional[Game]) -> None:
|
||||
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # Potential fix for 4K screens
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
@ -111,7 +111,7 @@ def run_ui(game: Optional[Game], new_map: bool) -> None:
|
||||
GameUpdateSignal()
|
||||
|
||||
# Start window
|
||||
window = QLiberationWindow(game, new_map)
|
||||
window = QLiberationWindow(game)
|
||||
window.showMaximized()
|
||||
splash.finish(window)
|
||||
qt_execution_code = app.exec_()
|
||||
@ -139,16 +139,8 @@ def parse_args() -> argparse.Namespace:
|
||||
help="Emits a warning for weapons without date or fallback information.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--new-map",
|
||||
action="store_true",
|
||||
default=True,
|
||||
help="Use the new map. Functional but missing many display options.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--old-map", dest="new_map", action="store_false", help="Use the old map."
|
||||
)
|
||||
parser.add_argument("--new-map", help="Deprecated. Does nothing.")
|
||||
parser.add_argument("--old-map", help="Deprecated. Does nothing.")
|
||||
|
||||
new_game = subparsers.add_parser("new-game")
|
||||
|
||||
@ -267,7 +259,7 @@ def main():
|
||||
args.cheats,
|
||||
)
|
||||
|
||||
run_ui(game, args.new_map)
|
||||
run_ui(game)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
from PySide2.QtGui import QColor, QFont, QPixmap
|
||||
from PySide2.QtGui import QPixmap
|
||||
|
||||
from game.theater.theatergroundobject import NAME_BY_CATEGORY
|
||||
from .liberation_theme import get_theme_icons
|
||||
@ -16,51 +16,6 @@ URLS: Dict[str, str] = {
|
||||
LABELS_OPTIONS = ["Full", "Abbreviated", "Dot Only", "Off"]
|
||||
SKILL_OPTIONS = ["Average", "Good", "High", "Excellent"]
|
||||
|
||||
FONT_SIZE = 8
|
||||
FONT_NAME = "Arial"
|
||||
# FONT = QFont("Arial", 12, weight=5, italic=True)
|
||||
FONT_PRIMARY = QFont(FONT_NAME, FONT_SIZE, weight=5, italic=False)
|
||||
FONT_PRIMARY_I = QFont(FONT_NAME, FONT_SIZE, weight=5, italic=True)
|
||||
FONT_PRIMARY_B = QFont(FONT_NAME, FONT_SIZE, weight=75, italic=False)
|
||||
FONT_MAP = QFont(FONT_NAME, 10, weight=75, italic=False)
|
||||
|
||||
COLORS: Dict[str, QColor] = {
|
||||
"white": QColor(255, 255, 255),
|
||||
"white_transparent": QColor(255, 255, 255, 35),
|
||||
"light_red": QColor(231, 92, 83, 90),
|
||||
"red": QColor(200, 80, 80),
|
||||
"dark_red": QColor(140, 20, 20),
|
||||
"red_transparent": QColor(227, 32, 0, 20),
|
||||
"transparent": QColor(255, 255, 255, 0),
|
||||
"light_blue": QColor(105, 182, 240, 90),
|
||||
"blue": QColor(0, 132, 255),
|
||||
"dark_blue": QColor(45, 62, 80),
|
||||
"sea_blue": QColor(52, 68, 85),
|
||||
"sea_blue_transparent": QColor(52, 68, 85, 150),
|
||||
"blue_transparent": QColor(0, 132, 255, 20),
|
||||
"purple": QColor(187, 137, 255),
|
||||
"yellow": QColor(238, 225, 123),
|
||||
"bright_red": QColor(150, 80, 80),
|
||||
"super_red": QColor(227, 32, 0),
|
||||
"green": QColor(128, 186, 128),
|
||||
"light_green": QColor(223, 255, 173),
|
||||
"light_green_transparent": QColor(180, 255, 140, 50),
|
||||
"bright_green": QColor(64, 200, 64),
|
||||
"black": QColor(0, 0, 0),
|
||||
"black_transparent": QColor(0, 0, 0, 5),
|
||||
"orange": QColor(254, 125, 10),
|
||||
"night_overlay": QColor(12, 20, 69),
|
||||
"dawn_dust_overlay": QColor(46, 38, 85),
|
||||
"grey": QColor(150, 150, 150),
|
||||
"grey_transparent": QColor(150, 150, 150, 150),
|
||||
"dark_grey": QColor(75, 75, 75),
|
||||
"dark_grey_transparent": QColor(75, 75, 75, 150),
|
||||
"dark_dark_grey": QColor(48, 48, 48),
|
||||
"dark_dark_grey_transparent": QColor(48, 48, 48, 150),
|
||||
}
|
||||
|
||||
CP_SIZE = 12
|
||||
|
||||
AIRCRAFT_BANNERS: Dict[str, QPixmap] = {}
|
||||
AIRCRAFT_ICONS: Dict[str, QPixmap] = {}
|
||||
VEHICLE_BANNERS: Dict[str, QPixmap] = {}
|
||||
@ -138,17 +93,6 @@ def load_icons():
|
||||
"./resources/ui/misc/" + get_theme_icons() + "/ordnance_icon.png"
|
||||
)
|
||||
|
||||
ICONS["target"] = QPixmap("./resources/ui/ground_assets/target.png")
|
||||
ICONS["cleared"] = QPixmap("./resources/ui/ground_assets/cleared.png")
|
||||
for category in NAME_BY_CATEGORY.keys():
|
||||
ICONS[category] = QPixmap("./resources/ui/ground_assets/" + category + ".png")
|
||||
ICONS[category + "_blue"] = QPixmap(
|
||||
"./resources/ui/ground_assets/" + category + "_blue.png"
|
||||
)
|
||||
ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.png")
|
||||
ICONS["nothreat"] = QPixmap("./resources/ui/ground_assets/nothreat.png")
|
||||
ICONS["nothreat_blue"] = QPixmap("./resources/ui/ground_assets/nothreat_blue.png")
|
||||
|
||||
ICONS["Generator"] = QPixmap(
|
||||
"./resources/ui/misc/" + get_theme_icons() + "/generator.png"
|
||||
)
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
from PySide2 import QtCore, QtGui, QtWidgets
|
||||
from PySide2 import QtCore, QtGui
|
||||
from PySide2.QtWidgets import QCalendarWidget
|
||||
|
||||
from qt_ui.uiconstants import COLORS
|
||||
|
||||
|
||||
class QLiberationCalendar(QCalendarWidget):
|
||||
def __init__(self, parent=None):
|
||||
@ -29,7 +27,7 @@ class QLiberationCalendar(QCalendarWidget):
|
||||
painter.save()
|
||||
painter.fillRect(rect, QtGui.QColor("#D3D3D3"))
|
||||
painter.setPen(QtCore.Qt.NoPen)
|
||||
painter.setBrush(QtGui.QColor(COLORS["sea_blue"]))
|
||||
painter.setBrush(QtGui.QColor(52, 68, 85))
|
||||
r = QtCore.QRect(
|
||||
QtCore.QPoint(), min(rect.width(), rect.height()) * QtCore.QSize(1, 1)
|
||||
)
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
"""Common base for objects drawn on the game map."""
|
||||
from typing import Optional
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtGui import QPen
|
||||
from PySide2.QtWidgets import (
|
||||
QAction,
|
||||
QGraphicsLineItem,
|
||||
QGraphicsSceneContextMenuEvent,
|
||||
QGraphicsSceneHoverEvent,
|
||||
QGraphicsSceneMouseEvent,
|
||||
QMenu,
|
||||
)
|
||||
|
||||
import qt_ui.uiconstants as const
|
||||
from game.theater import FrontLine
|
||||
from qt_ui.dialogs import Dialog
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
from qt_ui.windows.mission.QPackageDialog import QNewPackageDialog
|
||||
|
||||
|
||||
class QFrontLine(QGraphicsLineItem):
|
||||
"""Base class for objects drawn on the game map.
|
||||
|
||||
Game map objects have an on_click behavior that triggers on left click, and
|
||||
change the mouse cursor on hover.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
x1: float,
|
||||
y1: float,
|
||||
x2: float,
|
||||
y2: float,
|
||||
mission_target: FrontLine,
|
||||
game_model: GameModel,
|
||||
) -> None:
|
||||
super().__init__(x1, y1, x2, y2)
|
||||
self.mission_target = mission_target
|
||||
self.game_model = game_model
|
||||
self.new_package_dialog: Optional[QNewPackageDialog] = None
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
pen = QPen(brush=const.COLORS["bright_red"])
|
||||
pen.setColor(const.COLORS["orange"])
|
||||
pen.setWidth(8)
|
||||
self.setPen(pen)
|
||||
|
||||
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.on_click()
|
||||
|
||||
def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None:
|
||||
menu = QMenu("Menu")
|
||||
|
||||
object_details_action = QAction(self.object_dialog_text)
|
||||
object_details_action.triggered.connect(self.on_click)
|
||||
menu.addAction(object_details_action)
|
||||
|
||||
new_package_action = QAction(f"New package")
|
||||
new_package_action.triggered.connect(self.open_new_package_dialog)
|
||||
menu.addAction(new_package_action)
|
||||
|
||||
if self.game_model.game.settings.enable_frontline_cheats:
|
||||
cheat_forward = QAction(f"CHEAT: Advance Frontline")
|
||||
cheat_forward.triggered.connect(self.cheat_forward)
|
||||
menu.addAction(cheat_forward)
|
||||
|
||||
cheat_backward = QAction(f"CHEAT: Retreat Frontline")
|
||||
cheat_backward.triggered.connect(self.cheat_backward)
|
||||
menu.addAction(cheat_backward)
|
||||
|
||||
menu.exec_(event.screenPos())
|
||||
|
||||
@property
|
||||
def object_dialog_text(self) -> str:
|
||||
"""Text to for the object's dialog in the context menu.
|
||||
|
||||
Right clicking a map object will open a context menu and the first item
|
||||
will open the details dialog for this object. This menu action has the
|
||||
same behavior as the on_click event.
|
||||
|
||||
Return:
|
||||
The text that should be displayed for the menu item.
|
||||
"""
|
||||
return "Details"
|
||||
|
||||
def on_click(self) -> None:
|
||||
"""The action to take when this map object is left-clicked.
|
||||
|
||||
Typically this should open a details view of the object.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def open_new_package_dialog(self) -> None:
|
||||
"""Opens the dialog for planning a new mission package."""
|
||||
Dialog.open_new_package_dialog(self.mission_target)
|
||||
|
||||
def cheat_forward(self) -> None:
|
||||
self.mission_target.blue_cp.base.affect_strength(0.1)
|
||||
self.mission_target.red_cp.base.affect_strength(-0.1)
|
||||
# Clear the ATO to replan missions affected by the front line.
|
||||
self.game_model.game.reset_ato()
|
||||
self.game_model.game.initialize_turn()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||
|
||||
def cheat_backward(self) -> None:
|
||||
self.mission_target.blue_cp.base.affect_strength(-0.1)
|
||||
self.mission_target.red_cp.base.affect_strength(0.1)
|
||||
# Clear the ATO to replan missions affected by the front line.
|
||||
self.game_model.game.reset_ato()
|
||||
self.game_model.game.initialize_turn()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,21 +0,0 @@
|
||||
from PySide2.QtWidgets import QGraphicsScene, QGraphicsSceneMouseEvent
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
|
||||
|
||||
class QLiberationScene(QGraphicsScene):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
item = self.addText(
|
||||
'Go to "File/New Game" to setup a new campaign or go to "File/Open" to load an existing save game.',
|
||||
CONST.FONT_PRIMARY,
|
||||
)
|
||||
item.setDefaultTextColor(CONST.COLORS["white"])
|
||||
|
||||
def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):
|
||||
super(QLiberationScene, self).mouseMoveEvent(event)
|
||||
self.parent().sceneMouseMovedEvent(event)
|
||||
|
||||
def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
|
||||
super(QLiberationScene, self).mousePressEvent(event)
|
||||
self.parent().sceneMousePressEvent(event)
|
||||
@ -1,125 +0,0 @@
|
||||
from typing import Optional
|
||||
|
||||
from PySide2.QtGui import QColor, QPainter
|
||||
from PySide2.QtWidgets import QAction, QMenu
|
||||
|
||||
import qt_ui.uiconstants as const
|
||||
from game.theater import ControlPoint, NavalControlPoint
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2
|
||||
from .QMapObject import QMapObject
|
||||
from ...displayoptions import DisplayOptions
|
||||
from ...windows.GameUpdateSignal import GameUpdateSignal
|
||||
|
||||
|
||||
class QMapControlPoint(QMapObject):
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
x: float,
|
||||
y: float,
|
||||
w: float,
|
||||
h: float,
|
||||
control_point: ControlPoint,
|
||||
game_model: GameModel,
|
||||
) -> None:
|
||||
super().__init__(x, y, w, h, mission_target=control_point)
|
||||
self.game_model = game_model
|
||||
self.control_point = control_point
|
||||
self.parent = parent
|
||||
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)
|
||||
|
||||
self.move_action = QAction("Move")
|
||||
self.move_action.triggered.connect(self.move)
|
||||
|
||||
self.cancel_move_action = QAction("Cancel Move")
|
||||
self.cancel_move_action.triggered.connect(self.cancel_move)
|
||||
|
||||
def paint(self, painter, option, widget=None) -> None:
|
||||
if DisplayOptions.control_points:
|
||||
painter.save()
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
painter.setBrush(self.brush_color)
|
||||
painter.setPen(self.pen_color)
|
||||
|
||||
if not self.control_point.runway_is_operational():
|
||||
painter.setBrush(const.COLORS["black"])
|
||||
painter.setPen(self.brush_color)
|
||||
|
||||
r = option.rect
|
||||
painter.drawEllipse(r.x(), r.y(), r.width(), r.height())
|
||||
# TODO: Draw sunk carriers differently.
|
||||
# Either don't draw them at all, or perhaps use a sunk ship icon.
|
||||
painter.restore()
|
||||
|
||||
@property
|
||||
def brush_color(self) -> QColor:
|
||||
if self.control_point.captured:
|
||||
return const.COLORS["blue"]
|
||||
else:
|
||||
return const.COLORS["super_red"]
|
||||
|
||||
@property
|
||||
def pen_color(self) -> QColor:
|
||||
return const.COLORS["white"]
|
||||
|
||||
@property
|
||||
def object_dialog_text(self) -> str:
|
||||
if self.control_point.captured:
|
||||
return "Open base menu"
|
||||
else:
|
||||
return "Open intel menu"
|
||||
|
||||
def on_click(self) -> None:
|
||||
self.base_details_dialog = QBaseMenu2(
|
||||
self.window(), self.control_point, self.game_model
|
||||
)
|
||||
self.base_details_dialog.show()
|
||||
|
||||
def add_context_menu_actions(self, menu: QMenu) -> None:
|
||||
|
||||
if self.control_point.moveable and self.control_point.captured:
|
||||
menu.addAction(self.move_action)
|
||||
if self.control_point.target_position is not None:
|
||||
menu.addAction(self.cancel_move_action)
|
||||
|
||||
if self.control_point.is_fleet:
|
||||
return
|
||||
|
||||
if self.control_point.captured:
|
||||
return
|
||||
|
||||
for connected in self.control_point.connected_points:
|
||||
if (
|
||||
connected.captured
|
||||
and self.game_model.game.settings.enable_base_capture_cheat
|
||||
):
|
||||
menu.addAction(self.capture_action)
|
||||
break
|
||||
|
||||
def cheat_capture(self) -> None:
|
||||
self.control_point.capture(self.game_model.game, for_player=True)
|
||||
# Reinitialized ground planners and the like. The ATO needs to be reset because
|
||||
# missions planned against the flipped base are no longer valid.
|
||||
self.game_model.game.reset_ato()
|
||||
self.game_model.game.initialize_turn()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||
|
||||
def move(self):
|
||||
self.parent.setSelectedUnit(self)
|
||||
|
||||
def cancel_move(self):
|
||||
self.control_point.target_position = None
|
||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||
|
||||
def open_new_package_dialog(self) -> None:
|
||||
"""Extends the default packagedialog to redirect to base menu for red air base."""
|
||||
is_navy = isinstance(self.control_point, NavalControlPoint)
|
||||
if self.control_point.captured or is_navy:
|
||||
super().open_new_package_dialog()
|
||||
return
|
||||
self.on_click()
|
||||
@ -1,167 +0,0 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from PySide2.QtCore import QRect
|
||||
from PySide2.QtGui import QBrush
|
||||
from PySide2.QtWidgets import QGraphicsItem
|
||||
|
||||
import qt_ui.uiconstants as const
|
||||
from game import Game
|
||||
from game.data.building_data import FORTIFICATION_BUILDINGS
|
||||
from game.db import REWARDS
|
||||
from game.theater import ControlPoint, TheaterGroundObject
|
||||
from game.theater.theatergroundobject import (
|
||||
MissileSiteGroundObject,
|
||||
CoastalSiteGroundObject,
|
||||
)
|
||||
from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu
|
||||
from .QMapObject import QMapObject
|
||||
from ...displayoptions import DisplayOptions
|
||||
|
||||
|
||||
class QMapGroundObject(QMapObject):
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
x: float,
|
||||
y: float,
|
||||
w: float,
|
||||
h: float,
|
||||
control_point: ControlPoint,
|
||||
ground_object: TheaterGroundObject,
|
||||
game: Game,
|
||||
buildings: Optional[List[TheaterGroundObject]] = None,
|
||||
) -> None:
|
||||
super().__init__(x, y, w, h, mission_target=ground_object)
|
||||
self.ground_object = ground_object
|
||||
self.control_point = control_point
|
||||
self.parent = parent
|
||||
self.game = game
|
||||
self.setZValue(2)
|
||||
self.buildings = buildings if buildings is not None else []
|
||||
self.setFlag(QGraphicsItem.ItemIgnoresTransformations, False)
|
||||
self.ground_object_dialog: Optional[QGroundObjectMenu] = None
|
||||
self.setToolTip(self.tooltip)
|
||||
|
||||
@property
|
||||
def tooltip(self) -> str:
|
||||
lines = [
|
||||
f"[{self.ground_object.obj_name}]",
|
||||
f"${self.production_per_turn} per turn",
|
||||
]
|
||||
if self.ground_object.groups:
|
||||
units = {}
|
||||
for g in self.ground_object.groups:
|
||||
for u in g.units:
|
||||
if u.type in units:
|
||||
units[u.type] = units[u.type] + 1
|
||||
else:
|
||||
units[u.type] = 1
|
||||
|
||||
for unit in units.keys():
|
||||
lines.append(f"{unit} x {units[unit]}")
|
||||
else:
|
||||
for building in self.buildings:
|
||||
if not building.is_dead:
|
||||
lines.append(f"{building.dcs_identifier}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
@property
|
||||
def production_per_turn(self) -> int:
|
||||
production = 0
|
||||
for building in self.buildings:
|
||||
if building.is_dead:
|
||||
continue
|
||||
if building.category in REWARDS.keys():
|
||||
production += REWARDS[building.category]
|
||||
return production
|
||||
|
||||
def paint(self, painter, option, widget=None) -> None:
|
||||
player_icons = "_blue"
|
||||
enemy_icons = ""
|
||||
|
||||
if DisplayOptions.ground_objects:
|
||||
painter.save()
|
||||
|
||||
cat = self.ground_object.category
|
||||
|
||||
rect = QRect(
|
||||
option.rect.x() + 2,
|
||||
option.rect.y(),
|
||||
option.rect.width() - 2,
|
||||
option.rect.height(),
|
||||
)
|
||||
|
||||
is_dead = self.ground_object.is_dead
|
||||
for building in self.buildings:
|
||||
if not building.is_dead:
|
||||
is_dead = False
|
||||
break
|
||||
|
||||
if cat == "aa":
|
||||
has_threat = False
|
||||
for group in self.ground_object.groups:
|
||||
if self.ground_object.threat_range(group).distance_in_meters > 0:
|
||||
has_threat = True
|
||||
|
||||
if not is_dead and not self.control_point.captured:
|
||||
if cat == "aa" and not has_threat:
|
||||
painter.drawPixmap(rect, const.ICONS["nothreat" + enemy_icons])
|
||||
else:
|
||||
painter.drawPixmap(rect, const.ICONS[cat + enemy_icons])
|
||||
elif not is_dead:
|
||||
if cat == "aa" and not has_threat:
|
||||
painter.drawPixmap(rect, const.ICONS["nothreat" + player_icons])
|
||||
else:
|
||||
painter.drawPixmap(rect, const.ICONS[cat + player_icons])
|
||||
else:
|
||||
painter.drawPixmap(rect, const.ICONS["destroyed"])
|
||||
|
||||
self.draw_health_gauge(painter, option)
|
||||
painter.restore()
|
||||
|
||||
def draw_health_gauge(self, painter, option) -> None:
|
||||
units_alive = 0
|
||||
units_dead = 0
|
||||
|
||||
if len(self.ground_object.groups) == 0:
|
||||
for building in self.buildings:
|
||||
if building.dcs_identifier in FORTIFICATION_BUILDINGS:
|
||||
continue
|
||||
if building.is_dead:
|
||||
units_dead += 1
|
||||
else:
|
||||
units_alive += 1
|
||||
|
||||
for g in self.ground_object.groups:
|
||||
units_alive += len(g.units)
|
||||
if hasattr(g, "units_losts"):
|
||||
units_dead += len(g.units_losts)
|
||||
|
||||
if units_dead + units_alive > 0:
|
||||
ratio = float(units_alive) / (float(units_dead) + float(units_alive))
|
||||
bar_height = ratio * option.rect.height()
|
||||
painter.fillRect(
|
||||
option.rect.x(),
|
||||
option.rect.y(),
|
||||
2,
|
||||
option.rect.height(),
|
||||
QBrush(const.COLORS["dark_red"]),
|
||||
)
|
||||
painter.fillRect(
|
||||
option.rect.x(),
|
||||
option.rect.y(),
|
||||
2,
|
||||
bar_height,
|
||||
QBrush(const.COLORS["green"]),
|
||||
)
|
||||
|
||||
def on_click(self) -> None:
|
||||
self.ground_object_dialog = QGroundObjectMenu(
|
||||
self.window(),
|
||||
self.ground_object,
|
||||
self.buildings,
|
||||
self.control_point,
|
||||
self.game,
|
||||
)
|
||||
self.ground_object_dialog.show()
|
||||
@ -1,84 +0,0 @@
|
||||
"""Common base for objects drawn on the game map."""
|
||||
from typing import Optional
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtWidgets import (
|
||||
QAction,
|
||||
QGraphicsRectItem,
|
||||
QGraphicsSceneContextMenuEvent,
|
||||
QGraphicsSceneHoverEvent,
|
||||
QGraphicsSceneMouseEvent,
|
||||
QMenu,
|
||||
)
|
||||
|
||||
from qt_ui.dialogs import Dialog
|
||||
from qt_ui.windows.mission.QPackageDialog import QNewPackageDialog
|
||||
from game.theater.missiontarget import MissionTarget
|
||||
|
||||
|
||||
class QMapObject(QGraphicsRectItem):
|
||||
"""Base class for objects drawn on the game map.
|
||||
|
||||
Game map objects have an on_click behavior that triggers on left click, and
|
||||
change the mouse cursor on hover.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, x: float, y: float, w: float, h: float, mission_target: MissionTarget
|
||||
) -> None:
|
||||
super().__init__(x, y, w, h)
|
||||
self.mission_target = mission_target
|
||||
self.new_package_dialog: Optional[QNewPackageDialog] = None
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
|
||||
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)
|
||||
|
||||
object_details_action = QAction(self.object_dialog_text)
|
||||
object_details_action.triggered.connect(self.on_click)
|
||||
menu.addAction(object_details_action)
|
||||
|
||||
# Not all locations have valid objectives. Off-map spawns, for example,
|
||||
# have no mission types.
|
||||
if list(self.mission_target.mission_types(for_player=True)):
|
||||
new_package_action = QAction(f"New package")
|
||||
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
|
||||
def object_dialog_text(self) -> str:
|
||||
"""Text to for the object's dialog in the context menu.
|
||||
|
||||
Right clicking a map object will open a context menu and the first item
|
||||
will open the details dialog for this object. This menu action has the
|
||||
same behavior as the on_click event.
|
||||
|
||||
Return:
|
||||
The text that should be displayed for the menu item.
|
||||
"""
|
||||
return "Details"
|
||||
|
||||
def on_click(self) -> None:
|
||||
"""The action to take when this map object is left-clicked.
|
||||
|
||||
Typically this should open a details view of the object.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def open_new_package_dialog(self) -> None:
|
||||
"""Opens the dialog for planning a new mission package."""
|
||||
Dialog.open_new_package_dialog(self.mission_target)
|
||||
@ -1,70 +0,0 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtGui import QColor, QPen
|
||||
from PySide2.QtWidgets import (
|
||||
QGraphicsItem,
|
||||
QGraphicsLineItem,
|
||||
)
|
||||
|
||||
from game.theater import ControlPoint
|
||||
from game.transfers import CargoShip
|
||||
from qt_ui.uiconstants import COLORS
|
||||
|
||||
|
||||
class ShippingLaneSegment(QGraphicsLineItem):
|
||||
def __init__(
|
||||
self,
|
||||
x0: float,
|
||||
y0: float,
|
||||
x1: float,
|
||||
y1: float,
|
||||
control_point_a: ControlPoint,
|
||||
control_point_b: ControlPoint,
|
||||
ships: List[CargoShip],
|
||||
parent: Optional[QGraphicsItem] = None,
|
||||
) -> None:
|
||||
super().__init__(x0, y0, x1, y1, parent)
|
||||
self.control_point_a = control_point_a
|
||||
self.control_point_b = control_point_b
|
||||
self.ships = ships
|
||||
self.setPen(self.make_pen())
|
||||
self.setToolTip(self.make_tooltip())
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
@property
|
||||
def has_ships(self) -> bool:
|
||||
return bool(self.ships)
|
||||
|
||||
def make_tooltip(self) -> str:
|
||||
if not self.has_ships:
|
||||
return "No ships present in this shipping lane."
|
||||
|
||||
ships = []
|
||||
for ship in self.ships:
|
||||
units = "units" if ship.size > 1 else "unit"
|
||||
ships.append(
|
||||
f"{ship.size} {units} transferring from {ship.origin} to "
|
||||
f"{ship.destination}."
|
||||
)
|
||||
return "\n".join(ships)
|
||||
|
||||
@property
|
||||
def line_color(self) -> QColor:
|
||||
if self.control_point_a.captured:
|
||||
return COLORS["dark_blue"]
|
||||
else:
|
||||
return COLORS["dark_red"]
|
||||
|
||||
@property
|
||||
def line_style(self) -> Qt.PenStyle:
|
||||
if self.has_ships:
|
||||
return Qt.PenStyle.SolidLine
|
||||
return Qt.PenStyle.DotLine
|
||||
|
||||
def make_pen(self) -> QPen:
|
||||
pen = QPen(brush=self.line_color)
|
||||
pen.setColor(self.line_color)
|
||||
pen.setStyle(self.line_style)
|
||||
pen.setWidth(2)
|
||||
return pen
|
||||
@ -1,76 +0,0 @@
|
||||
from functools import cached_property
|
||||
from typing import List, Optional
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtGui import QColor, QPen
|
||||
from PySide2.QtWidgets import (
|
||||
QGraphicsItem,
|
||||
QGraphicsLineItem,
|
||||
)
|
||||
|
||||
from game.theater import ControlPoint
|
||||
from game.transfers import Convoy
|
||||
from qt_ui.uiconstants import COLORS
|
||||
|
||||
|
||||
class SupplyRouteSegment(QGraphicsLineItem):
|
||||
def __init__(
|
||||
self,
|
||||
x0: float,
|
||||
y0: float,
|
||||
x1: float,
|
||||
y1: float,
|
||||
control_point_a: ControlPoint,
|
||||
control_point_b: ControlPoint,
|
||||
convoys: List[Convoy],
|
||||
parent: Optional[QGraphicsItem] = None,
|
||||
) -> None:
|
||||
super().__init__(x0, y0, x1, y1, parent)
|
||||
self.control_point_a = control_point_a
|
||||
self.control_point_b = control_point_b
|
||||
self.convoys = convoys
|
||||
self.setPen(self.make_pen())
|
||||
self.setToolTip(self.make_tooltip())
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
@property
|
||||
def has_convoys(self) -> bool:
|
||||
return bool(self.convoys)
|
||||
|
||||
def make_tooltip(self) -> str:
|
||||
if not self.has_convoys:
|
||||
return "No convoys present on this supply route."
|
||||
|
||||
convoys = []
|
||||
for convoy in self.convoys:
|
||||
units = "units" if convoy.size > 1 else "unit"
|
||||
convoys.append(
|
||||
f"{convoy.size} {units} transferring from {convoy.origin} to "
|
||||
f"{convoy.destination}"
|
||||
)
|
||||
return "\n".join(convoys)
|
||||
|
||||
@property
|
||||
def line_color(self) -> QColor:
|
||||
if self.control_point_a.front_is_active(self.control_point_b):
|
||||
return COLORS["red"]
|
||||
elif self.control_point_a.captured:
|
||||
return COLORS["dark_blue"]
|
||||
else:
|
||||
return COLORS["dark_red"]
|
||||
|
||||
@property
|
||||
def line_style(self) -> Qt.PenStyle:
|
||||
if (
|
||||
self.control_point_a.front_is_active(self.control_point_b)
|
||||
or self.has_convoys
|
||||
):
|
||||
return Qt.PenStyle.SolidLine
|
||||
return Qt.PenStyle.DotLine
|
||||
|
||||
def make_pen(self) -> QPen:
|
||||
pen = QPen(brush=self.line_color)
|
||||
pen.setColor(self.line_color)
|
||||
pen.setStyle(self.line_style)
|
||||
pen.setWidth(6)
|
||||
return pen
|
||||
@ -36,6 +36,8 @@ from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu
|
||||
LeafletLatLon = list[float]
|
||||
LeafletPoly = list[LeafletLatLon]
|
||||
|
||||
MAX_SHIP_DISTANCE = nautical_miles(80)
|
||||
|
||||
# **EVERY PROPERTY NEEDS A NOTIFY SIGNAL**
|
||||
#
|
||||
# https://bugreports.qt.io/browse/PYSIDE-1426
|
||||
|
||||
@ -22,12 +22,11 @@ from game import Game, VERSION, persistency
|
||||
from game.debriefing import Debriefing
|
||||
from qt_ui import liberation_install
|
||||
from qt_ui.dialogs import Dialog
|
||||
from qt_ui.displayoptions import DisplayGroup, DisplayOptions, DisplayRule
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.uiconstants import URLS
|
||||
from qt_ui.widgets.QTopPanel import QTopPanel
|
||||
from qt_ui.widgets.ato import QAirTaskingOrderPanel
|
||||
from qt_ui.widgets.map.QLiberationMap import LeafletMap, QLiberationMap, LiberationMap
|
||||
from qt_ui.widgets.map.QLiberationMap import QLiberationMap
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
from qt_ui.windows.QDebriefingWindow import QDebriefingWindow
|
||||
from qt_ui.windows.infos.QInfoPanel import QInfoPanel
|
||||
@ -38,7 +37,7 @@ from qt_ui.windows.preferences.QLiberationPreferencesWindow import (
|
||||
|
||||
|
||||
class QLiberationWindow(QMainWindow):
|
||||
def __init__(self, game: Optional[Game], new_map: bool) -> None:
|
||||
def __init__(self, game: Optional[Game]) -> None:
|
||||
super(QLiberationWindow, self).__init__()
|
||||
|
||||
self.game = game
|
||||
@ -46,7 +45,7 @@ class QLiberationWindow(QMainWindow):
|
||||
Dialog.set_game(self.game_model)
|
||||
self.ato_panel = QAirTaskingOrderPanel(self.game_model)
|
||||
self.info_panel = QInfoPanel(self.game)
|
||||
self.liberation_map: LiberationMap = self.create_map(new_map)
|
||||
self.liberation_map = QLiberationMap(self.game_model, self)
|
||||
|
||||
self.setGeometry(300, 100, 270, 100)
|
||||
self.setWindowTitle(f"DCS Liberation - v{VERSION}")
|
||||
@ -174,30 +173,6 @@ class QLiberationWindow(QMainWindow):
|
||||
file_menu.addSeparator()
|
||||
file_menu.addAction("E&xit", self.close)
|
||||
|
||||
displayMenu = self.menu.addMenu("&Display")
|
||||
|
||||
last_was_group = False
|
||||
for item in DisplayOptions.menu_items():
|
||||
if isinstance(item, DisplayRule):
|
||||
if last_was_group:
|
||||
displayMenu.addSeparator()
|
||||
self.display_bar.addSeparator()
|
||||
action = self.make_display_rule_action(item)
|
||||
displayMenu.addAction(action)
|
||||
if action.icon():
|
||||
self.display_bar.addAction(action)
|
||||
last_was_group = False
|
||||
elif isinstance(item, DisplayGroup):
|
||||
displayMenu.addSeparator()
|
||||
self.display_bar.addSeparator()
|
||||
group = QActionGroup(displayMenu)
|
||||
for display_rule in item:
|
||||
action = self.make_display_rule_action(display_rule, group)
|
||||
displayMenu.addAction(action)
|
||||
if action.icon():
|
||||
self.display_bar.addAction(action)
|
||||
last_was_group = True
|
||||
|
||||
help_menu = self.menu.addMenu("&Help")
|
||||
help_menu.addAction(self.openDiscordAction)
|
||||
help_menu.addAction(self.openGithubAction)
|
||||
@ -284,11 +259,6 @@ class QLiberationWindow(QMainWindow):
|
||||
self.game = game
|
||||
GameUpdateSignal.get_instance().game_loaded.emit(self.game)
|
||||
|
||||
def create_map(self, new_map: bool) -> LiberationMap:
|
||||
if new_map:
|
||||
return LeafletMap(self.game_model, self)
|
||||
return QLiberationMap(self.game_model)
|
||||
|
||||
def setGame(self, game: Optional[Game]):
|
||||
try:
|
||||
self.game = game
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user