dcs-retribution/qt_ui/widgets/map/QLiberationMap.py
2020-06-28 01:06:02 -04:00

348 lines
14 KiB
Python

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.QMapEvent import QMapEvent
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] + 100, 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] - 100, 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] + 100
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] - 100
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] + 100, 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] - 100, 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] + 100
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] - 100
print(self.game.theater.reference_points)
self.reload_scene()
"""
def reload_scene(self):
scene = self.scene()
scene.clear()
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["blue"])
brush = CONST.COLORS["blue_transparent"]
else:
pen = QPen(brush=CONST.COLORS["red"])
brush = CONST.COLORS["red_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, brush, brush)
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)
for cp in self.game.theater.player_points():
if self.get_display_rule("lines"):
self.scene_create_lines_for_cp(cp)
for cp in self.game.theater.controlpoints:
if cp.captured:
pen = QPen(brush=CONST.COLORS["blue"])
brush = CONST.COLORS["blue_transparent"]
flight_path_pen = QPen(brush=CONST.COLORS["blue"])
flight_path_pen.setColor(CONST.COLORS["blue"])
flight_path_pen.setWidth(1)
flight_path_pen.setStyle(Qt.DashDotLine)
else:
pen = QPen(brush=CONST.COLORS["red"])
brush = CONST.COLORS["red_transparent"]
flight_path_pen = QPen(brush=CONST.COLORS["bright_red"])
flight_path_pen.setColor(CONST.COLORS["bright_red"])
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):
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_red"]
elif cp.captured:
color = CONST.COLORS["dark_blue"]
else:
color = CONST.COLORS["dark_red"]
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(18)
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]