mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
349 lines
14 KiB
Python
349 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.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]
|