class QBaseDefenseGroupInfo(QGroupBox): def __init__(self, cp: ControlPoint, ground_object: TheaterGroundObject, game): super(QBaseDefenseGroupInfo, self).__init__("Group : " + ground_object.obj_name) self.ground_object = ground_object self.cp = cp self.game = game self.buildings = game.theater.find_ground_objects_by_obj_name( self.ground_object.obj_name) self.init_ui() def init_ui(self): unit_dict = {} layout = QGridLayout() for g in self.ground_object.groups: for u in g.units: if u.type in unit_dict.keys(): unit_dict[u.type] = unit_dict[u.type] + 1 else: unit_dict[u.type] = 1 i = 0 for k, v in unit_dict.items(): #icon = QLabel() #if k in VEHICLES_ICONS.keys(): # icon.setPixmap(VEHICLES_ICONS[k]) #else: # icon.setText("<b>" + k[:6] + "</b>") #icon.setProperty("style", "icon-plane") #layout.addWidget(icon, i, 0) layout.addWidget( QLabel(str(v) + " x " + "<strong>" + k + "</strong>"), i, 0) i = i + 1 manage_button = QPushButton("Manage") manage_button.setProperty("style", "btn-success") manage_button.setMaximumWidth(180) manage_button.clicked.connect(self.onManage) layout.addWidget(manage_button, i + 1, 0) self.setLayout(layout) def onManage(self): self.editionMenu = QGroundObjectMenu(self.window(), self.ground_object, self.buildings, self.cp, self.game) self.editionMenu.show()
class QBaseDefenseGroupInfo(QGroupBox): def __init__(self, cp: ControlPoint, ground_object: TheaterGroundObject, game): super(QBaseDefenseGroupInfo, self).__init__("Group : " + ground_object.obj_name) self.ground_object = ground_object self.cp = cp self.game = game self.buildings = game.theater.find_ground_objects_by_obj_name( self.ground_object.obj_name) self.main_layout = QVBoxLayout() self.unit_layout = QGridLayout() self.init_ui() def init_ui(self): self.buildLayout() self.main_layout.addLayout(self.unit_layout) if not self.cp.captured and not self.ground_object.is_dead: attack_button = QPushButton("Attack") attack_button.setProperty("style", "btn-danger") attack_button.setMaximumWidth(180) attack_button.clicked.connect(self.onAttack) self.main_layout.addWidget(attack_button, 0, Qt.AlignLeft) if self.cp.captured: manage_button = QPushButton("Manage") manage_button.setProperty("style", "btn-success") manage_button.setMaximumWidth(180) manage_button.clicked.connect(self.onManage) self.main_layout.addWidget(manage_button, 0, Qt.AlignLeft) self.setLayout(self.main_layout) def buildLayout(self): unit_dict = {} for i in range(self.unit_layout.rowCount()): for j in range(self.unit_layout.columnCount()): item = self.unit_layout.itemAtPosition(i, j) if item is not None and item.widget() is not None: item.widget().setParent(None) print("Remove " + str(i) + ", " + str(j)) for g in self.ground_object.groups: for u in g.units: if u.type in unit_dict.keys(): unit_dict[u.type] = unit_dict[u.type] + 1 else: unit_dict[u.type] = 1 i = 0 for k, v in unit_dict.items(): icon = QLabel() if k in VEHICLES_ICONS.keys(): icon.setPixmap(VEHICLES_ICONS[k]) else: icon.setText("<b>" + k[:8] + "</b>") icon.setProperty("style", "icon-armor") self.unit_layout.addWidget(icon, i, 0) unit_display_name = k unit_type = vehicles.vehicle_map.get(k) if unit_type is not None: unit_display_name = db.unit_get_expanded_info( self.game.enemy_country, unit_type, 'name') self.unit_layout.addWidget( QLabel( str(v) + " x " + "<strong>" + unit_display_name + "</strong>"), i, 1) i = i + 1 if len(unit_dict.items()) == 0: self.unit_layout.addWidget(QLabel("/"), 0, 0) self.setLayout(self.main_layout) def onAttack(self): Dialog.open_new_package_dialog(self.ground_object, parent=self.window()) def onManage(self): self.edition_menu = QGroundObjectMenu(self.window(), self.ground_object, self.buildings, self.cp, self.game) self.edition_menu.show() self.edition_menu.changed.connect(self.onEdition) def onEdition(self): self.buildLayout()
class GroundObjectJs(QObject): nameChanged = Signal() controlPointNameChanged = Signal() unitsChanged = Signal() blueChanged = Signal() positionChanged = Signal() samThreatRangesChanged = Signal() samDetectionRangesChanged = Signal() categoryChanged = Signal() deadChanged = Signal() def __init__(self, tgo: TheaterGroundObject, game: Game) -> None: super().__init__() self.tgo = tgo self.game = game self.theater = game.theater self.buildings = self.theater.find_ground_objects_by_obj_name(self.tgo.obj_name) self.dialog: Optional[QGroundObjectMenu] = None @Slot() def showInfoDialog(self) -> None: if self.dialog is None: self.dialog = QGroundObjectMenu( None, self.tgo, self.buildings, self.tgo.control_point, self.game, ) self.dialog.show() @Slot() def showPackageDialog(self) -> None: Dialog.open_new_package_dialog(self.tgo) @Property(str, notify=nameChanged) def name(self) -> str: return self.tgo.name @Property(str, notify=controlPointNameChanged) def controlPointName(self) -> str: return self.tgo.control_point.name @Property(str, notify=categoryChanged) def category(self) -> str: return self.tgo.category @staticmethod def make_unit_name(unit: Unit, dead: bool) -> str: dead_label = " [DEAD]" if dead else "" unit_display_name = unit.type dcs_unit_type = vehicle_map.get(unit.type) if dcs_unit_type is not None: # TODO: Make the TGO contain GroundUnitType instead of the pydcs Group. # This is a hack because we can't know which variant was used. try: unit_display_name = next( GroundUnitType.for_dcs_type(dcs_unit_type) ).name except StopIteration: pass return f"Unit #{unit.id} - {unit_display_name}{dead_label}" @Property(list, notify=unitsChanged) def units(self) -> List[str]: units = [] # TGOs with a non-empty group set are non-building TGOs. Building TGOs have no # groups set, but instead are one TGO per building "group" (DCS doesn't support # groups of statics) all with the same name. if self.tgo.groups: for unit in self.tgo.units: units.append(self.make_unit_name(unit, dead=False)) for unit in self.tgo.dead_units: units.append(self.make_unit_name(unit, dead=True)) else: for building in self.buildings: dead = " [DEAD]" if building.is_dead else "" units.append(f"{building.dcs_identifier}{dead}") return units @Property(bool, notify=blueChanged) def blue(self) -> bool: return self.tgo.control_point.captured @Property(list, notify=positionChanged) def position(self) -> LeafletLatLon: ll = self.theater.point_to_ll(self.tgo.position) return [ll.latitude, ll.longitude] @Property(bool, notify=deadChanged) def dead(self) -> bool: if not self.tgo.groups: return all(b.is_dead for b in self.buildings) return not any(g.units for g in self.tgo.groups) @Property(list, notify=samThreatRangesChanged) def samThreatRanges(self) -> List[float]: if not self.tgo.might_have_aa: return [] ranges = [] for group in self.tgo.groups: threat_range = self.tgo.threat_range(group) if threat_range: ranges.append(threat_range.meters) return ranges @Property(list, notify=samDetectionRangesChanged) def samDetectionRanges(self) -> List[float]: if not self.tgo.might_have_aa: return [] ranges = [] for group in self.tgo.groups: detection_range = self.tgo.detection_range(group) if detection_range: ranges.append(detection_range.meters) return ranges
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 if cat == "aa" and self.ground_object.sea_object: cat = "ship" if isinstance(self.ground_object, MissileSiteGroundObject): cat = "missile" 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()
class QMapGroundObject(QGraphicsRectItem): def __init__(self, parent, x: float, y: float, w: float, h: float, cp: ControlPoint, model: TheaterGroundObject, game:Game, buildings=[]): super(QMapGroundObject, self).__init__(x, y, w, h) self.model = model self.cp = cp self.parent = parent self.game = game self.setAcceptHoverEvents(True) self.setZValue(2) self.buildings = buildings self.setFlag(QGraphicsItem.ItemIgnoresTransformations, False) if len(self.model.groups) > 0: units = {} for g in self.model.groups: print(g) for u in g.units: if u.type in units.keys(): units[u.type] = units[u.type]+1 else: units[u.type] = 1 tooltip = "[" + self.model.obj_name + "]" + "\n" for unit in units.keys(): tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n" self.setToolTip(tooltip[:-1]) else: tooltip = "[" + self.model.obj_name + "]" + "\n" for building in buildings: if not building.is_dead: tooltip = tooltip + str(building.dcs_identifier) + "\n" self.setToolTip(tooltip[:-1]) def mousePressEvent(self, event:QGraphicsSceneMouseEvent): self.openEditionMenu() def paint(self, painter, option, widget=None): #super(QMapControlPoint, self).paint(painter, option, widget) playerIcons = "_blue" enemyIcons = "" if self.parent.get_display_rule("go"): painter.save() cat = self.model.category if cat == "aa" and self.model.sea_object: cat = "ship" rect = QRect(option.rect.x()+2,option.rect.y(),option.rect.width()-2,option.rect.height()) is_dead = self.model.is_dead for building in self.buildings: if not building.is_dead: is_dead = False break if not is_dead and not self.cp.captured: painter.drawPixmap(rect, CONST.ICONS[cat + enemyIcons]) elif not is_dead: painter.drawPixmap(rect, CONST.ICONS[cat + playerIcons]) else: painter.drawPixmap(rect, CONST.ICONS["destroyed"]) self.drawHealthGauge(painter, option) painter.restore() def drawHealthGauge(self, painter, option): units_alive = 0 units_dead = 0 if len(self.model.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.model.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 hoverEnterEvent(self, event: QGraphicsSceneHoverEvent): self.update() self.setCursor(Qt.PointingHandCursor) def mouseMoveEvent(self, event:QGraphicsSceneMouseEvent): self.update() self.setCursor(Qt.PointingHandCursor) def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): self.update() def openEditionMenu(self): self.editionMenu = QGroundObjectMenu(self.window(), self.model, self.buildings, self.cp, self.game) self.editionMenu.show()