import typing from typing import Dict from PySide2 import QtCore from PySide2.QtCore import Qt, QRect, QPointF from PySide2.QtGui import QPixmap, QBrush, QColor, QWheelEvent, QPen, QFont from PySide2.QtWidgets import QGraphicsView, QFrame, QGraphicsOpacityEffect from dcs import Point from dcs.mapping import point_from_heading import qt_ui.uiconstants as CONST from game import Game, db from game.data.radar_db import UNITS_WITH_RADAR from game.event import UnitsDeliveryEvent, Event, ControlPointType from gen import Conflict from qt_ui.widgets.map.QLiberationScene import QLiberationScene from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint from qt_ui.widgets.map.QMapGroundObject import QMapGroundObject from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from theater import ControlPoint class QLiberationMap(QGraphicsView): instance = None display_rules: Dict[str, bool] = { "cp": True, "go": True, "lines": True, "events": True, "sam": True, "flight_paths": False } def __init__(self, game: Game): super(QLiberationMap, self).__init__() QLiberationMap.instance = self self.frontline_vector_cache = {} self.setMinimumSize(800,600) self.setMaximumHeight(2160) self._zoom = 0 self.factor = 1 self.factorized = 1 self.init_scene() self.connectSignals() self.setGame(game) def init_scene(self): scene = QLiberationScene(self) self.setScene(scene) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setBackgroundBrush(QBrush(QColor(30, 30, 30))) self.setFrameShape(QFrame.NoFrame) self.setDragMode(QGraphicsView.ScrollHandDrag) def connectSignals(self): GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) def setGame(self, game: Game): self.game = game print("Reloading Map Canvas") if self.game is not None: self.reload_scene() """ Uncomment to set up theather reference points def keyPressEvent(self, event): #super(QLiberationMap, self).keyPressEvent(event) numpad_mod = int(event.modifiers()) & QtCore.Qt.KeypadModifier i = 0 for k,v in self.game.theater.reference_points.items(): if i == 0: point_0 = k else: point_1 = k i = i + 1 if event.key() == QtCore.Qt.Key_Down: self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0] + 10, self.game.theater.reference_points[point_0][1] if event.key() == QtCore.Qt.Key_Up: self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0] - 10, self.game.theater.reference_points[point_0][1] if event.key() == QtCore.Qt.Key_Left: self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0], self.game.theater.reference_points[point_0][1] + 10 if event.key() == QtCore.Qt.Key_Right: self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0], self.game.theater.reference_points[point_0][1] - 10 if event.key() == QtCore.Qt.Key_2 and numpad_mod: self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0] + 10, self.game.theater.reference_points[point_1][1] if event.key() == QtCore.Qt.Key_8 and numpad_mod: self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0] - 10, self.game.theater.reference_points[point_1][1] if event.key() == QtCore.Qt.Key_4 and numpad_mod: self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0], self.game.theater.reference_points[point_1][1] + 10 if event.key() == QtCore.Qt.Key_6 and numpad_mod: self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0], self.game.theater.reference_points[point_1][1] - 10 print(self.game.theater.reference_points) self.reload_scene() """ def reload_scene(self): scene = self.scene() scene.clear() playerColor = self.game.get_player_color() enemyColor = self.game.get_enemy_color() self.addBackground() # Uncomment below to help set up theater reference points #for i, r in enumerate(self.game.theater.reference_points.items()): # text = scene.addText(str(r), font=QFont("Trebuchet MS", 10, weight=5, italic=False)) # text.setPos(0, i * 24) for cp in self.game.theater.controlpoints: pos = self._transform_point(cp.position) scene.addItem(QMapControlPoint(self, pos[0] - CONST.CP_SIZE / 2, pos[1] - CONST.CP_SIZE / 2, CONST.CP_SIZE, CONST.CP_SIZE, cp, self.game)) if cp.captured: pen = QPen(brush=CONST.COLORS[playerColor]) brush = CONST.COLORS[playerColor+"_transparent"] else: pen = QPen(brush=CONST.COLORS[enemyColor]) brush = CONST.COLORS[enemyColor+"_transparent"] added_objects = [] for ground_object in cp.ground_objects: if ground_object.obj_name in added_objects: continue go_pos = self._transform_point(ground_object.position) if not ground_object.airbase_group: buildings = self.game.theater.find_ground_objects_by_obj_name(ground_object.obj_name) scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object, buildings)) if ground_object.category == "aa" and self.get_display_rule("sam"): max_range = 0 has_radar = False if ground_object.groups: for g in ground_object.groups: for u in g.units: unit = db.unit_type_from_name(u.type) if unit in UNITS_WITH_RADAR: has_radar = True if unit.threat_range > max_range: max_range = unit.threat_range if has_radar: scene.addEllipse(go_pos[0] - max_range/300.0 + 8, go_pos[1] - max_range/300.0 + 8, max_range/150.0, max_range/150.0, CONST.COLORS["white_transparent"], CONST.COLORS["grey_transparent"]) added_objects.append(ground_object.obj_name) for cp in self.game.theater.enemy_points(): if self.get_display_rule("lines"): self.scene_create_lines_for_cp(cp, playerColor, enemyColor) for cp in self.game.theater.player_points(): if self.get_display_rule("lines"): self.scene_create_lines_for_cp(cp, playerColor, enemyColor) for cp in self.game.theater.controlpoints: if cp.captured: pen = QPen(brush=CONST.COLORS[playerColor]) brush = CONST.COLORS[playerColor+"_transparent"] flight_path_pen = QPen(brush=CONST.COLORS[playerColor]) flight_path_pen.setColor(CONST.COLORS[playerColor]) else: pen = QPen(brush=CONST.COLORS[enemyColor]) brush = CONST.COLORS[enemyColor+"_transparent"] flight_path_pen = QPen(brush=CONST.COLORS[enemyColor]) flight_path_pen.setColor(CONST.COLORS[enemyColor]) flight_path_pen.setWidth(1) flight_path_pen.setStyle(Qt.DashDotLine) pos = self._transform_point(cp.position) if self.get_display_rule("flight_paths"): if cp.id in self.game.planners.keys(): planner = self.game.planners[cp.id] for flight in planner.flights: scene.addEllipse(pos[0], pos[1], 4, 4) prev_pos = list(pos) for point in flight.points: new_pos = self._transform_point(Point(point.x, point.y)) scene.addLine(prev_pos[0]+2, prev_pos[1]+2, new_pos[0]+2, new_pos[1]+2, flight_path_pen) scene.addEllipse(new_pos[0], new_pos[1], 4, 4, pen, brush) prev_pos = list(new_pos) scene.addLine(prev_pos[0] + 2, prev_pos[1] + 2, pos[0] + 2, pos[1] + 2, flight_path_pen) for cp in self.game.theater.controlpoints: pos = self._transform_point(cp.position) text = scene.addText(cp.name, font=CONST.FONT_MAP) text.setPos(pos[0] + CONST.CP_SIZE, pos[1] - CONST.CP_SIZE / 2) text = scene.addText(cp.name, font=CONST.FONT_MAP) text.setDefaultTextColor(Qt.white) text.setPos(pos[0] + CONST.CP_SIZE + 1, pos[1] - CONST.CP_SIZE / 2 + 1) def scene_create_lines_for_cp(self, cp: ControlPoint, playerColor, enemyColor): scene = self.scene() pos = self._transform_point(cp.position) for connected_cp in cp.connected_points: pos2 = self._transform_point(connected_cp.position) if not cp.captured: color = CONST.COLORS["dark_"+enemyColor] elif cp.captured: color = CONST.COLORS["dark_"+playerColor] else: color = CONST.COLORS["dark_"+enemyColor] pen = QPen(brush=color) pen.setColor(color) pen.setWidth(6) if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp): if not cp.captured: scene.addLine(pos[0], pos[1], pos2[0], pos2[1], pen=pen) else: posx, h = Conflict.frontline_position(self.game.theater, cp, connected_cp) pos2 = self._transform_point(posx) scene.addLine(pos[0], pos[1], pos2[0], pos2[1], pen=pen) p1 = point_from_heading(pos2[0], pos2[1], h+180, 25) p2 = point_from_heading(pos2[0], pos2[1], h, 25) frontline_pen = QPen(brush=CONST.COLORS["bright_red"]) frontline_pen.setColor(CONST.COLORS["orange"]) frontline_pen.setWidth(8) scene.addLine(p1[0], p1[1], p2[0], p2[1], pen=frontline_pen) else: scene.addLine(pos[0], pos[1], pos2[0], pos2[1], pen=pen) def _frontline_vector(self, from_cp: ControlPoint, to_cp: ControlPoint): # Cache mechanism to avoid performing frontline vector computation on every frame key = str(from_cp.id) + "_" + str(to_cp.id) if key in self.frontline_vector_cache: return self.frontline_vector_cache[key] else: frontline = Conflict.frontline_vector(from_cp, to_cp, self.game.theater) self.frontline_vector_cache[key] = frontline return frontline def _frontline_center(self, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[Point]: frontline_vector = self._frontline_vector(from_cp, to_cp) if frontline_vector: return frontline_vector[0].point_from_heading(frontline_vector[1], frontline_vector[2]/2) else: return None def wheelEvent(self, event: QWheelEvent): if event.angleDelta().y() > 0: factor = 1.25 self._zoom += 1 if self._zoom < 10: self.scale(factor, factor) self.factorized *= factor else: self._zoom = 9 else: factor = 0.8 self._zoom -= 1 if self._zoom > -5: self.scale(factor, factor) self.factorized *= factor else: self._zoom = -4 #print(self.factorized, factor, self._zoom) def _transform_point(self, p: Point, treshold=30) -> (int, int): point_a = list(self.game.theater.reference_points.keys())[0] point_a_img = self.game.theater.reference_points[point_a] point_b = list(self.game.theater.reference_points.keys())[1] point_b_img = self.game.theater.reference_points[point_b] Y_dist = point_a_img[0] - point_b_img[0] lon_dist = point_a[1] - point_b[1] X_dist = point_a_img[1] - point_b_img[1] lat_dist = point_b[0] - point_a[0] Y_scale = float(Y_dist) / float(lon_dist) X_scale = float(X_dist) / float(lat_dist) # --- Y_offset = p.x - point_a[0] X_offset = p.y - point_a[1] X = point_b_img[1] + X_offset * X_scale Y = point_a_img[0] - Y_offset * Y_scale #X += 5 #Y += 5 return X > treshold and X or treshold, Y > treshold and Y or treshold def addBackground(self): scene = self.scene() bg = QPixmap("./resources/" + self.game.theater.overview_image) scene.addPixmap(bg) # Apply graphical effects to simulate current daytime if self.game.current_turn_daytime == "day": pass elif self.game.current_turn_daytime == "night": ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["night_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.7) overlay.setGraphicsEffect(effect) else: ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["dawn_dust_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.3) overlay.setGraphicsEffect(effect) @staticmethod def set_display_rule(rule: str, value: bool): QLiberationMap.display_rules[rule] = value QLiberationMap.instance.reload_scene() QLiberationMap.instance.update() @staticmethod def get_display_rules() -> Dict[str, bool]: return QLiberationMap.display_rules @staticmethod def get_display_rule(rule) -> bool: return QLiberationMap.display_rules[rule]