This commit is contained in:
Dan Albert
2021-02-12 19:58:30 -08:00
parent 053663bd76
commit a47bef1f13
222 changed files with 8434 additions and 4461 deletions

View File

@@ -35,7 +35,9 @@ class QBudgetBox(QGroupBox):
:param budget: Current money available
:param reward: Planned reward for next turn
"""
self.money_amount.setText(str(round(budget,2)) + "M (+" + str(round(reward,2)) + "M)")
self.money_amount.setText(
str(round(budget, 2)) + "M (+" + str(round(reward, 2)) + "M)"
)
def setGame(self, game):
if game is None:
@@ -47,4 +49,4 @@ class QBudgetBox(QGroupBox):
def openFinances(self):
self.subwindow = QFinancesMenu(self.game)
self.subwindow.show()
self.subwindow.show()

View File

@@ -18,10 +18,12 @@ class QTimeTurnWidget(QGroupBox):
"""
UI Component to display current turn and time info
"""
def __init__(self):
super(QTimeTurnWidget, self).__init__("Turn")
self.setStyleSheet('padding: 0px; margin-left: 5px; margin-right: 0px; margin-top: 1ex; margin-bottom: 5px; border-right: 0px')
self.setStyleSheet(
"padding: 0px; margin-left: 5px; margin-right: 0px; margin-top: 1ex; margin-bottom: 5px; border-right: 0px"
)
self.icons = {
TimeOfDay.Dawn: CONST.ICONS["Dawn"],
@@ -55,20 +57,21 @@ class QTimeTurnWidget(QGroupBox):
"""
self.daytime_icon.setPixmap(self.icons[conditions.time_of_day])
self.date_display.setText(conditions.start_time.strftime("%d %b %Y"))
self.time_display.setText(
conditions.start_time.strftime("%H:%M:%S Local"))
self.time_display.setText(conditions.start_time.strftime("%H:%M:%S Local"))
self.setTitle(f"Turn {turn}")
class QWeatherWidget(QGroupBox):
"""
UI Component to display current weather forecast
"""
turn = None
conditions = None
def __init__(self):
super(QWeatherWidget, self).__init__("")
self.setProperty('style', 'QWeatherWidget')
self.setProperty("style", "QWeatherWidget")
self.icons = {
TimeOfDay.Dawn: CONST.ICONS["Dawn"],
@@ -83,17 +86,15 @@ class QWeatherWidget(QGroupBox):
self.makeWeatherIcon()
self.makeCloudRainFogWidget()
self.makeWindsWidget()
def makeWeatherIcon(self):
"""Makes the Weather Icon Widget
"""
"""Makes the Weather Icon Widget"""
self.weather_icon = QLabel()
self.weather_icon.setPixmap(self.icons[TimeOfDay.Dawn])
self.layout.addWidget(self.weather_icon)
def makeCloudRainFogWidget(self):
"""Makes the Cloud, Rain, Fog Widget
"""
"""Makes the Cloud, Rain, Fog Widget"""
self.textLayout = QVBoxLayout()
self.layout.addLayout(self.textLayout)
@@ -107,43 +108,41 @@ class QWeatherWidget(QGroupBox):
self.textLayout.addWidget(self.forecastFog)
def makeWindsWidget(self):
"""Factory for the winds widget.
"""
"""Factory for the winds widget."""
windsLayout = QGridLayout()
self.layout.addLayout(windsLayout)
windsLayout.addWidget(self.makeIcon(CONST.ICONS['Weather_winds']), 0, 0, 3, 1)
windsLayout.addWidget(self.makeIcon(CONST.ICONS["Weather_winds"]), 0, 0, 3, 1)
windsLayout.addWidget(self.makeLabel('At GL'), 0, 1)
windsLayout.addWidget(self.makeLabel('At FL08'), 1, 1)
windsLayout.addWidget(self.makeLabel('At FL26'), 2, 1)
windsLayout.addWidget(self.makeLabel("At GL"), 0, 1)
windsLayout.addWidget(self.makeLabel("At FL08"), 1, 1)
windsLayout.addWidget(self.makeLabel("At FL26"), 2, 1)
self.windGLSpeedLabel = self.makeLabel('0kts')
self.windGLDirLabel = self.makeLabel('')
self.windGLSpeedLabel = self.makeLabel("0kts")
self.windGLDirLabel = self.makeLabel("")
windsLayout.addWidget(self.windGLSpeedLabel, 0, 2)
windsLayout.addWidget(self.windGLDirLabel, 0, 3)
self.windFL08SpeedLabel = self.makeLabel('0kts')
self.windFL08DirLabel = self.makeLabel('')
self.windFL08SpeedLabel = self.makeLabel("0kts")
self.windFL08DirLabel = self.makeLabel("")
windsLayout.addWidget(self.windFL08SpeedLabel, 1, 2)
windsLayout.addWidget(self.windFL08DirLabel, 1, 3)
self.windFL26SpeedLabel = self.makeLabel('0kts')
self.windFL26DirLabel = self.makeLabel('')
self.windFL26SpeedLabel = self.makeLabel("0kts")
self.windFL26DirLabel = self.makeLabel("")
windsLayout.addWidget(self.windFL26SpeedLabel, 2, 2)
windsLayout.addWidget(self.windFL26DirLabel, 2, 3)
def makeLabel(self, text: str = '') -> QLabel:
def makeLabel(self, text: str = "") -> QLabel:
"""Shorthand to generate a QLabel with widget standard style
:arg pixmap QPixmap for the icon.
"""
label = QLabel(text)
label.setProperty('style', 'text-sm')
label.setProperty("style", "text-sm")
return label
def makeIcon(self, pixmap: QPixmap) -> QLabel:
"""Shorthand to generate a QIcon with pixmap.
@@ -167,26 +166,28 @@ class QWeatherWidget(QGroupBox):
self.updateWinds()
def updateWinds(self):
"""Updates the UI with the current conditions wind info.
"""
"""Updates the UI with the current conditions wind info."""
windGlSpeed = mps(self.conditions.weather.wind.at_0m.speed or 0)
windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, '0')
self.windGLSpeedLabel.setText(f'{int(windGlSpeed.knots)}kts')
self.windGLDirLabel.setText(f'{windGlDir}º')
windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, "0")
self.windGLSpeedLabel.setText(f"{int(windGlSpeed.knots)}kts")
self.windGLDirLabel.setText(f"{windGlDir}º")
windFL08Speed = mps(self.conditions.weather.wind.at_2000m.speed or 0)
windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust(3, '0')
self.windFL08SpeedLabel.setText(f'{int(windFL08Speed.knots)}kts')
self.windFL08DirLabel.setText(f'{windFL08Dir}º')
windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust(
3, "0"
)
self.windFL08SpeedLabel.setText(f"{int(windFL08Speed.knots)}kts")
self.windFL08DirLabel.setText(f"{windFL08Dir}º")
windFL26Speed = mps(self.conditions.weather.wind.at_8000m.speed or 0)
windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust(3, '0')
self.windFL26SpeedLabel.setText(f'{int(windFL26Speed.knots)}kts')
self.windFL26DirLabel.setText(f'{windFL26Dir}º')
windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust(
3, "0"
)
self.windFL26SpeedLabel.setText(f"{int(windFL26Speed.knots)}kts")
self.windFL26DirLabel.setText(f"{windFL26Dir}º")
def updateForecast(self):
"""Updates the Forecast Text and icon with the current conditions wind info.
"""
"""Updates the Forecast Text and icon with the current conditions wind info."""
icon = []
if self.conditions.weather.clouds is None:
cloudDensity = 0
@@ -197,44 +198,44 @@ class QWeatherWidget(QGroupBox):
fog = self.conditions.weather.fog or None
is_night = self.conditions.time_of_day == TimeOfDay.Night
time = 'night' if is_night else 'day'
time = "night" if is_night else "day"
if cloudDensity <= 0:
self.forecastClouds.setText('Sunny')
icon = [time, 'clear']
self.forecastClouds.setText("Sunny")
icon = [time, "clear"]
if cloudDensity > 0 and cloudDensity < 3:
self.forecastClouds.setText('Partly Cloudy')
icon = [time, 'partly-cloudy']
self.forecastClouds.setText("Partly Cloudy")
icon = [time, "partly-cloudy"]
if cloudDensity >= 3 and cloudDensity < 5:
self.forecastClouds.setText('Mostly Cloudy')
icon = [time, 'partly-cloudy']
self.forecastClouds.setText("Mostly Cloudy")
icon = [time, "partly-cloudy"]
if cloudDensity >= 5:
self.forecastClouds.setText('Totally Cloudy')
icon = [time, 'partly-cloudy']
self.forecastClouds.setText("Totally Cloudy")
icon = [time, "partly-cloudy"]
if precipitation == PydcsWeather.Preceptions.Rain:
self.forecastRain.setText('Rain')
icon = [time, 'rain']
self.forecastRain.setText("Rain")
icon = [time, "rain"]
elif precipitation == PydcsWeather.Preceptions.Thunderstorm:
self.forecastRain.setText('Thunderstorm')
icon = [time, 'thunderstorm']
self.forecastRain.setText("Thunderstorm")
icon = [time, "thunderstorm"]
else:
self.forecastRain.setText('No Rain')
self.forecastRain.setText("No Rain")
if not fog:
self.forecastFog.setText('No fog')
else:
self.forecastFog.setText("No fog")
else:
visibility = round(fog.visibility.nautical_miles, 1)
self.forecastFog.setText(f'Fog vis: {visibility}nm')
icon = [time, ('cloudy' if cloudDensity > 1 else None), 'fog']
self.forecastFog.setText(f"Fog vis: {visibility}nm")
icon = [time, ("cloudy" if cloudDensity > 1 else None), "fog"]
icon_key = "Weather_{}".format('-'.join(filter(None.__ne__, icon)))
icon = CONST.ICONS.get(icon_key) or CONST.ICONS['Weather_night-partly-cloudy']
icon_key = "Weather_{}".format("-".join(filter(None.__ne__, icon)))
icon = CONST.ICONS.get(icon_key) or CONST.ICONS["Weather_night-partly-cloudy"]
self.weather_icon.setPixmap(icon)
@@ -245,7 +246,7 @@ class QConditionsWidget(QFrame):
def __init__(self):
super(QConditionsWidget, self).__init__()
self.setProperty('style', 'QConditionsWidget')
self.setProperty("style", "QConditionsWidget")
self.layout = QGridLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
@@ -254,11 +255,13 @@ class QConditionsWidget(QFrame):
self.setLayout(self.layout)
self.time_turn_widget = QTimeTurnWidget()
self.time_turn_widget.setStyleSheet('QGroupBox { margin-right: 0px; }')
self.time_turn_widget.setStyleSheet("QGroupBox { margin-right: 0px; }")
self.layout.addWidget(self.time_turn_widget, 0, 0)
self.weather_widget = QWeatherWidget()
self.weather_widget.setStyleSheet('QGroupBox { margin-top: 5px; margin-left: 0px; border-left: 0px; }')
self.weather_widget.setStyleSheet(
"QGroupBox { margin-top: 5px; margin-left: 0px; border-left: 0px; }"
)
self.weather_widget.hide()
self.layout.addWidget(self.weather_widget, 0, 1)
@@ -271,4 +274,3 @@ class QConditionsWidget(QFrame):
self.time_turn_widget.setCurrentTurn(turn, conditions)
self.weather_widget.setCurrentTurn(turn, conditions)
self.weather_widget.show()

View File

@@ -8,4 +8,4 @@ class QDebriefingInformation(QFrame):
def __init__(self):
super(QDebriefingInformation, self).__init__()
self.init_ui()
self.init_ui()

View File

@@ -16,10 +16,10 @@ class QFactionsInfos(QGroupBox):
self.layout = QGridLayout()
self.layout.setSpacing(0)
self.layout.addWidget(QLabel("<b>Player : </b>"),0,0)
self.layout.addWidget(self.player_name,0,1)
self.layout.addWidget(QLabel("<b>Enemy : </b>"),1,0)
self.layout.addWidget(self.enemy_name,1,1)
self.layout.addWidget(QLabel("<b>Player : </b>"), 0, 0)
self.layout.addWidget(self.player_name, 0, 1)
self.layout.addWidget(QLabel("<b>Enemy : </b>"), 1, 0)
self.layout.addWidget(self.enemy_name, 1, 1)
self.setLayout(self.layout)
def setGame(self, game: Game):
@@ -29,4 +29,3 @@ class QFactionsInfos(QGroupBox):
else:
self.player_name.setText("")
self.enemy_name.setText("")

View File

@@ -5,8 +5,9 @@ from PySide2.QtWidgets import QSpinBox
class QFlightSizeSpinner(QSpinBox):
"""Spin box for selecting the number of aircraft in a flight."""
def __init__(self, min_size: int = 1, max_size: int = 4,
default_size: int = 2) -> None:
def __init__(
self, min_size: int = 1, max_size: int = 4, default_size: int = 2
) -> None:
super().__init__()
self.setMinimum(min_size)
self.setMaximum(max_size)

View File

@@ -94,12 +94,16 @@ class QIntelBox(QGroupBox):
data = self.game.game_stats.data_per_turn[-1]
self.air_strength.setText(self.forces_strength_text(
data.allied_units.aircraft_count,
data.enemy_units.aircraft_count))
self.ground_strength.setText(self.forces_strength_text(
data.allied_units.vehicles_count,
data.enemy_units.vehicles_count))
self.air_strength.setText(
self.forces_strength_text(
data.allied_units.aircraft_count, data.enemy_units.aircraft_count
)
)
self.ground_strength.setText(
self.forces_strength_text(
data.allied_units.vehicles_count, data.enemy_units.vehicles_count
)
)
self.economic_strength.setText(self.economic_strength_text())
def open_details_window(self) -> None:

View File

@@ -12,8 +12,9 @@ class QLabeledWidget(QHBoxLayout):
label is used to name the input.
"""
def __init__(self, text: str, widget: QWidget,
tooltip: Optional[str] = None) -> None:
def __init__(
self, text: str, widget: QWidget, tooltip: Optional[str] = None
) -> None:
super().__init__()
label = QLabel(text)
self.addWidget(label)

View File

@@ -22,15 +22,13 @@ from qt_ui.widgets.QFactionsInfos import QFactionsInfos
from qt_ui.widgets.QIntelBox import QIntelBox
from qt_ui.widgets.clientslots import MaxPlayerCount
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.QWaitingForMissionResultWindow import \
QWaitingForMissionResultWindow
from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow
from qt_ui.windows.settings.QSettingsWindow import QSettingsWindow
from qt_ui.windows.stats.QStatsWindow import QStatsWindow
from qt_ui.widgets.QConditionsWidget import QConditionsWidget
class QTopPanel(QFrame):
def __init__(self, game_model: GameModel):
super(QTopPanel, self).__init__()
self.game_model = game_model
@@ -43,7 +41,7 @@ class QTopPanel(QFrame):
def game(self) -> Optional[Game]:
return self.game_model.game
def init_ui(self):
def init_ui(self):
self.conditionsWidget = QConditionsWidget()
self.budgetBox = QBudgetBox(self.game)
@@ -85,14 +83,13 @@ class QTopPanel(QFrame):
self.proceedBox = QGroupBox("Proceed")
self.proceedBoxLayout = QHBoxLayout()
self.proceedBoxLayout.addLayout(
MaxPlayerCount(self.game_model.ato_model))
self.proceedBoxLayout.addLayout(MaxPlayerCount(self.game_model.ato_model))
self.proceedBoxLayout.addWidget(self.passTurnButton)
self.proceedBoxLayout.addWidget(self.proceedButton)
self.proceedBox.setLayout(self.proceedBoxLayout)
self.layout = QHBoxLayout()
self.layout.addWidget(self.factionsInfos)
self.layout.addWidget(self.conditionsWidget)
self.layout.addWidget(self.budgetBox)
@@ -101,8 +98,8 @@ class QTopPanel(QFrame):
self.layout.addStretch(1)
self.layout.addWidget(self.proceedBox)
self.layout.setContentsMargins(0,0,0,0)
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)
def setGame(self, game: Optional[Game]):
@@ -169,48 +166,50 @@ class QTopPanel(QFrame):
result = QMessageBox.question(
self,
"Continue without client slots?",
("No client slots have been created for players. Continuing will "
"allow the AI to perform the mission, but players will be unable "
"to participate.<br />"
"<br />"
"To add client slots for players, select a package from the "
"Packages panel on the left of the main window, and then a flight "
"from the Flights panel below the Packages panel. The edit button "
"below the Flights panel will allow you to edit the number of "
"client slots in the flight. Each client slot allows one player.<br />"
"<br />Click 'Yes' to continue with an AI only mission"
"<br />Click 'No' if you'd like to make more changes."),
(
"No client slots have been created for players. Continuing will "
"allow the AI to perform the mission, but players will be unable "
"to participate.<br />"
"<br />"
"To add client slots for players, select a package from the "
"Packages panel on the left of the main window, and then a flight "
"from the Flights panel below the Packages panel. The edit button "
"below the Flights panel will allow you to edit the number of "
"client slots in the flight. Each client slot allows one player.<br />"
"<br />Click 'Yes' to continue with an AI only mission"
"<br />Click 'No' if you'd like to make more changes."
),
QMessageBox.No,
QMessageBox.Yes
QMessageBox.Yes,
)
return result == QMessageBox.Yes
def confirm_negative_start_time(self,
negative_starts: List[Package]) -> bool:
formatted = '<br />'.join(
def confirm_negative_start_time(self, negative_starts: List[Package]) -> bool:
formatted = "<br />".join(
[f"{p.primary_task} {p.target.name}" for p in negative_starts]
)
mbox = QMessageBox(
QMessageBox.Question,
"Continue with past start times?",
("Some flights in the following packages have start times set "
"earlier than mission start time:<br />"
"<br />"
f"{formatted}<br />"
"<br />"
"Flight start times are estimated based on the package TOT, so it "
"is possible that not all flights will be able to reach the "
"target area at their assigned times.<br />"
"<br />"
"You can either continue with the mission as planned, with the "
"misplanned flights potentially flying too fast and/or missing "
"their rendezvous; automatically fix negative TOTs; or cancel "
"mission start and fix the packages manually."),
parent=self
(
"Some flights in the following packages have start times set "
"earlier than mission start time:<br />"
"<br />"
f"{formatted}<br />"
"<br />"
"Flight start times are estimated based on the package TOT, so it "
"is possible that not all flights will be able to reach the "
"target area at their assigned times.<br />"
"<br />"
"You can either continue with the mission as planned, with the "
"misplanned flights potentially flying too fast and/or missing "
"their rendezvous; automatically fix negative TOTs; or cancel "
"mission start and fix the packages manually."
),
parent=self,
)
auto = mbox.addButton("Fix TOTs automatically", QMessageBox.ActionRole)
ignore = mbox.addButton("Continue without fixing",
QMessageBox.DestructiveRole)
ignore = mbox.addButton("Continue without fixing", QMessageBox.DestructiveRole)
cancel = mbox.addButton(QMessageBox.Cancel)
mbox.setEscapeButton(cancel)
mbox.exec_()
@@ -238,12 +237,12 @@ class QTopPanel(QFrame):
closest_cps[1],
self.game.theater.controlpoints[0].position,
self.game.player_name,
self.game.enemy_name)
self.game.enemy_name,
)
unit_map = self.game.initiate_event(game_event)
waiting = QWaitingForMissionResultWindow(game_event, self.game,
unit_map)
waiting = QWaitingForMissionResultWindow(game_event, self.game, unit_map)
waiting.show()
def budget_update(self, game:Game):
def budget_update(self, game: Game):
self.budgetBox.setGame(game)

View File

@@ -71,34 +71,38 @@ class FlightDelegate(QStyledItemDelegate):
return f"From {origin} to {flight.arrival.name}"
return f"From {origin}"
def paint(self, painter: QPainter, option: QStyleOptionViewItem,
index: QModelIndex) -> None:
def paint(
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
) -> None:
# Draw the list item with all the default selection styling, but with an
# invalid index so text formatting is left to us.
super().paint(painter, option, QModelIndex())
rect = option.rect.adjusted(self.HMARGIN, self.VMARGIN, -self.HMARGIN,
-self.VMARGIN)
rect = option.rect.adjusted(
self.HMARGIN, self.VMARGIN, -self.HMARGIN, -self.VMARGIN
)
with painter_context(painter):
painter.setFont(self.get_font(option))
icon: Optional[QIcon] = index.data(Qt.DecorationRole)
if icon is not None:
icon.paint(painter, rect, Qt.AlignLeft | Qt.AlignVCenter,
self.icon_mode(option),
self.icon_state(option))
icon.paint(
painter,
rect,
Qt.AlignLeft | Qt.AlignVCenter,
self.icon_mode(option),
self.icon_state(option),
)
rect = rect.adjusted(self.icon_size(option).width() + self.HMARGIN,
0, 0, 0)
rect = rect.adjusted(self.icon_size(option).width() + self.HMARGIN, 0, 0, 0)
painter.drawText(rect, Qt.AlignLeft, self.first_row_text(index))
line2 = rect.adjusted(0, rect.height() / 2, 0, rect.height() / 2)
painter.drawText(line2, Qt.AlignLeft, self.second_row_text(index))
clients = self.num_clients(index)
if clients:
painter.drawText(rect, Qt.AlignRight,
f"Player Slots: {clients}")
painter.drawText(rect, Qt.AlignRight, f"Player Slots: {clients}")
def num_clients(self, index: QModelIndex) -> int:
flight = self.flight(index)
@@ -126,22 +130,24 @@ class FlightDelegate(QStyledItemDelegate):
else:
return icon_size
def sizeHint(self, option: QStyleOptionViewItem,
index: QModelIndex) -> QSize:
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
left = self.icon_size(option).width() + self.HMARGIN
metrics = QFontMetrics(self.get_font(option))
first = metrics.size(0, self.first_row_text(index))
second = metrics.size(0, self.second_row_text(index))
text_width = max(first.width(), second.width())
return QSize(left + text_width + 2 * self.HMARGIN,
first.height() + second.height() + 2 * self.VMARGIN)
return QSize(
left + text_width + 2 * self.HMARGIN,
first.height() + second.height() + 2 * self.VMARGIN,
)
class QFlightList(QListView):
"""List view for displaying the flights of a package."""
def __init__(self, game_model: GameModel,
package_model: Optional[PackageModel]) -> None:
def __init__(
self, game_model: GameModel, package_model: Optional[PackageModel]
) -> None:
super().__init__()
self.game_model = game_model
self.package_model = package_model
@@ -163,8 +169,7 @@ class QFlightList(QListView):
# noinspection PyUnresolvedReferences
model.deleted.connect(self.disconnect_model)
self.selectionModel().setCurrentIndex(
model.index(0, 0, QModelIndex()),
QItemSelectionModel.Select
model.index(0, 0, QModelIndex()), QItemSelectionModel.Select
)
def disconnect_model(self) -> None:
@@ -192,14 +197,15 @@ class QFlightList(QListView):
def edit_flight(self, index: QModelIndex) -> None:
from qt_ui.dialogs import Dialog
Dialog.open_edit_flight_dialog(
self.package_model, self.package_model.flight_at_index(index),
parent=self.window()
self.package_model,
self.package_model.flight_at_index(index),
parent=self.window(),
)
def delete_flight(self, index: QModelIndex) -> None:
self.game_model.game.aircraft_inventory.return_from_flight(
self.selected_item)
self.game_model.game.aircraft_inventory.return_from_flight(self.selected_item)
self.package_model.delete_flight_at_index(index)
GameUpdateSignal.get_instance().redraw_flight_paths()
@@ -226,8 +232,9 @@ class QFlightPanel(QGroupBox):
delete buttons for flight management.
"""
def __init__(self, game_model: GameModel,
package_model: Optional[PackageModel] = None) -> None:
def __init__(
self, game_model: GameModel, package_model: Optional[PackageModel] = None
) -> None:
super().__init__("Flights")
self.game_model = game_model
self.package_model = package_model
@@ -336,14 +343,16 @@ class PackageDelegate(QStyledItemDelegate):
package = self.package(index)
return f"TOT T+{package.time_over_target}"
def paint(self, painter: QPainter, option: QStyleOptionViewItem,
index: QModelIndex) -> None:
def paint(
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
) -> None:
# Draw the list item with all the default selection styling, but with an
# invalid index so text formatting is left to us.
super().paint(painter, option, QModelIndex())
rect = option.rect.adjusted(self.HMARGIN, self.VMARGIN, -self.HMARGIN,
-self.VMARGIN)
rect = option.rect.adjusted(
self.HMARGIN, self.VMARGIN, -self.HMARGIN, -self.VMARGIN
)
with painter_context(painter):
painter.setFont(self.get_font(option))
@@ -354,20 +363,20 @@ class PackageDelegate(QStyledItemDelegate):
clients = self.num_clients(index)
if clients:
painter.drawText(rect, Qt.AlignRight,
f"Player Slots: {clients}")
painter.drawText(rect, Qt.AlignRight, f"Player Slots: {clients}")
def num_clients(self, index: QModelIndex) -> int:
package = self.package(index)
return sum(f.client_count for f in package.flights)
def sizeHint(self, option: QStyleOptionViewItem,
index: QModelIndex) -> QSize:
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
metrics = QFontMetrics(self.get_font(option))
left = metrics.size(0, self.left_text(index))
right = metrics.size(0, self.right_text(index))
return QSize(max(left.width(), right.width()) + 2 * self.HMARGIN,
left.height() + right.height() + 2 * self.VMARGIN)
return QSize(
max(left.width(), right.width()) + 2 * self.HMARGIN,
left.height() + right.height() + 2 * self.VMARGIN,
)
class QPackageList(QListView):
@@ -393,19 +402,20 @@ class QPackageList(QListView):
def edit_package(self, index: QModelIndex) -> None:
from qt_ui.dialogs import Dialog
Dialog.open_edit_package_dialog(self.ato_model.get_package_model(index))
def delete_package(self, index: QModelIndex) -> None:
self.ato_model.delete_package_at_index(index)
GameUpdateSignal.get_instance().redraw_flight_paths()
def on_new_packages(self, _parent: QModelIndex, first: int,
_last: int) -> None:
def on_new_packages(self, _parent: QModelIndex, first: int, _last: int) -> None:
# Select the newly created pacakges. This should only ever happen due to
# the player saving a new package, so selecting it helps them view/edit
# it faster.
self.selectionModel().setCurrentIndex(self.model().index(first, 0),
QItemSelectionModel.Select)
self.selectionModel().setCurrentIndex(
self.model().index(first, 0), QItemSelectionModel.Select
)
def on_double_click(self, index: QModelIndex) -> None:
if not index.isValid():
@@ -533,8 +543,6 @@ class QAirTaskingOrderPanel(QSplitter):
"""Sets the newly selected flight for display in the bottom panel."""
index = self.package_panel.package_list.currentIndex()
if index.isValid():
self.flight_panel.set_package(
self.ato_model.get_package_model(index)
)
self.flight_panel.set_package(self.ato_model.get_package_model(index))
else:
self.flight_panel.set_package(None)

View File

@@ -11,9 +11,12 @@ class MaxPlayerCount(QLabeledWidget):
self.slots_label = QLabel(str(self.count_client_slots))
self.ato_model.client_slots_changed.connect(self.update_count)
super().__init__(
"Max Players:", self.slots_label,
("Total number of client slots. To add client slots, edit a flight "
"using the panel on the left.")
"Max Players:",
self.slots_label,
(
"Total number of client slots. To add client slots, edit a flight "
"using the panel on the left."
),
)
@property

View File

@@ -11,10 +11,16 @@ import gen.flights.ai_flight_planner_db
from game import Game, db
class QAircraftTypeSelector(QComboBox):
"""Combo box for selecting among the given aircraft types."""
def __init__(self, aircraft_types: Iterable[Type[FlyingType]], country: str, mission_type: str) -> None:
def __init__(
self,
aircraft_types: Iterable[Type[FlyingType]],
country: str,
mission_type: str,
) -> None:
super().__init__()
self.model().sort(0)
@@ -26,32 +32,72 @@ class QAircraftTypeSelector(QComboBox):
current_aircraft = self.currentData()
self.clear()
for aircraft in aircraft_types:
if mission_type in [FlightType.BARCAP, FlightType.ESCORT, FlightType.INTERCEPTION, FlightType.SWEEP, FlightType.TARCAP]:
if mission_type in [
FlightType.BARCAP,
FlightType.ESCORT,
FlightType.INTERCEPTION,
FlightType.SWEEP,
FlightType.TARCAP,
]:
if aircraft in gen.flights.ai_flight_planner_db.CAP_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
elif mission_type in [FlightType.CAS, FlightType.BAI, FlightType.OCA_AIRCRAFT]:
if aircraft in gen.flights.ai_flight_planner_db.CAS_CAPABLE or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
elif mission_type in [
FlightType.CAS,
FlightType.BAI,
FlightType.OCA_AIRCRAFT,
]:
if (
aircraft in gen.flights.ai_flight_planner_db.CAS_CAPABLE
or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE
):
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
elif mission_type in [FlightType.SEAD]:
if aircraft in gen.flights.ai_flight_planner_db.SEAD_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
elif mission_type in [FlightType.DEAD]:
if aircraft in gen.flights.ai_flight_planner_db.DEAD_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
elif mission_type in [FlightType.STRIKE]:
if aircraft in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
if (
aircraft in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE
or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE
):
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
elif mission_type in [FlightType.ANTISHIP]:
if aircraft in gen.flights.ai_flight_planner_db.ANTISHIP_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
elif mission_type in [FlightType.OCA_RUNWAY]:
if aircraft in gen.flights.ai_flight_planner_db.RUNWAY_ATTACK_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
elif mission_type in [FlightType.AEWC]:
if aircraft in gen.flights.ai_flight_planner_db.AEWC_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
self.addItem(
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
userData=aircraft,
)
current_aircraft_index = self.findData(current_aircraft)
if current_aircraft_index != -1:
self.setCurrentIndex(current_aircraft_index)
if self.count() == 0:
self.addItem("No capable aircraft available", userData=None)
self.addItem("No capable aircraft available", userData=None)

View File

@@ -14,8 +14,12 @@ class QArrivalAirfieldSelector(QComboBox):
aircraft type is able to land at.
"""
def __init__(self, destinations: Iterable[ControlPoint],
aircraft: Type[FlyingType], optional_text: str) -> None:
def __init__(
self,
destinations: Iterable[ControlPoint],
aircraft: Type[FlyingType],
optional_text: str,
) -> None:
super().__init__()
self.destinations = list(destinations)
self.aircraft = aircraft

View File

@@ -3,9 +3,16 @@ from PySide2.QtWidgets import QComboBox, QCompleter
class QFilteredComboBox(QComboBox):
def __init__(self, parent=None, include_targets=True, include_airbases=True,
include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True):
def __init__(
self,
parent=None,
include_targets=True,
include_airbases=True,
include_frontlines=True,
include_units=True,
include_enemy=True,
include_friendly=True,
):
super(QFilteredComboBox, self).__init__(parent)
self.setFocusPolicy(Qt.StrongFocus)

View File

@@ -18,9 +18,12 @@ class QOriginAirfieldSelector(QComboBox):
availability_changed = Signal(int)
def __init__(self, global_inventory: GlobalAircraftInventory,
origins: Iterable[ControlPoint],
aircraft: Type[FlyingType]) -> None:
def __init__(
self,
global_inventory: GlobalAircraftInventory,
origins: Iterable[ControlPoint],
aircraft: Type[FlyingType],
) -> None:
super().__init__()
self.global_inventory = global_inventory
self.origins = list(origins)

View File

@@ -9,9 +9,17 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
def __init__(self, game: Game, parent=None, include_targets=True, include_airbases=True,
include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True):
def __init__(
self,
game: Game,
parent=None,
include_targets=True,
include_airbases=True,
include_frontlines=True,
include_units=True,
include_enemy=True,
include_friendly=True,
):
super(QPredefinedWaypointSelectionComboBox, self).__init__(parent)
self.game = game
self.include_targets = include_targets
@@ -34,7 +42,11 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
waypoints = [first_waypoint]
if include_all_from_same_location:
for w in self.wpts:
if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name:
if (
w is not first_waypoint
and w.obj_name
and w.obj_name == first_waypoint.obj_name
):
waypoints.append(w)
return waypoints
@@ -53,14 +65,19 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
if self.include_frontlines:
for cp in self.game.theater.controlpoints:
if cp.captured:
enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured]
enemy_cp = [
ecp
for ecp in cp.connected_points
if ecp.captured != cp.captured
]
for ecp in enemy_cp:
pos = Conflict.frontline_position(cp, ecp, self.game.theater)[0]
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
pos.x,
pos.y,
Distance.from_meters(800))
Distance.from_meters(800),
)
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
wpt.alt_type = "RADIO"
wpt.pretty_name = wpt.name
@@ -69,14 +86,18 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
if self.include_targets:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
if (self.include_enemy and not cp.captured) or (
self.include_friendly and cp.captured
):
for ground_object in cp.ground_objects:
if not ground_object.is_dead and isinstance(ground_object, BuildingGroundObject):
if not ground_object.is_dead and isinstance(
ground_object, BuildingGroundObject
):
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
ground_object.position.x,
ground_object.position.y,
Distance.from_meters(0)
Distance.from_meters(0),
)
wpt.alt_type = "RADIO"
wpt.name = ground_object.waypoint_name
@@ -91,19 +112,31 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
if self.include_units:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
if (self.include_enemy and not cp.captured) or (
self.include_friendly and cp.captured
):
for ground_object in cp.ground_objects:
if not ground_object.is_dead and ground_object.dcs_identifier == "AA":
if (
not ground_object.is_dead
and ground_object.dcs_identifier == "AA"
):
for g in ground_object.groups:
for j, u in enumerate(g.units):
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
u.position.x,
u.position.y,
Distance.from_meters(0)
Distance.from_meters(0),
)
wpt.alt_type = "RADIO"
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
wpt.name = wpt.name = (
"["
+ str(ground_object.obj_name)
+ "] : "
+ u.type
+ " #"
+ str(j)
)
wpt.pretty_name = wpt.name
wpt.targets.append(u)
wpt.obj_name = ground_object.obj_name
@@ -116,17 +149,21 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
if self.include_airbases:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
if (self.include_enemy and not cp.captured) or (
self.include_friendly and cp.captured
):
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
cp.position.x,
cp.position.y,
Distance.from_meters(0)
Distance.from_meters(0),
)
wpt.alt_type = "RADIO"
wpt.name = cp.name
if cp.captured:
wpt.description = "Position of " + cp.name + " [Friendly Airbase]"
wpt.description = (
"Position of " + cp.name + " [Friendly Airbase]"
)
else:
wpt.description = "Position of " + cp.name + " [Enemy Airbase]"

View File

@@ -7,7 +7,6 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
class SEADTargetInfo:
def __init__(self):
self.name = ""
self.location = None
@@ -15,8 +14,8 @@ class SEADTargetInfo:
self.threat_range = 0
self.detection_range = 0
class QSEADTargetSelectionComboBox(QFilteredComboBox):
class QSEADTargetSelectionComboBox(QFilteredComboBox):
def __init__(self, game: Game, parent=None):
super(QSEADTargetSelectionComboBox, self).__init__(parent)
self.game = game
@@ -41,7 +40,8 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
return i + 1
for cp in self.game.theater.controlpoints:
if cp.captured: continue
if cp.captured:
continue
for g in cp.ground_objects:
radars = []
@@ -53,7 +53,10 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
utype = db.unit_type_from_name(u.type)
if utype in UNITS_WITH_RADAR:
if hasattr(utype, "detection_range") and utype.detection_range > 1000:
if (
hasattr(utype, "detection_range")
and utype.detection_range > 1000
):
if utype.detection_range > detection_range:
detection_range = utype.detection_range
radars.append(u)
@@ -63,9 +66,18 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
threat_range = utype.threat_range
if len(radars) > 0:
tgt_info = SEADTargetInfo()
tgt_info.name = g.obj_name + " [" + ",".join([db.unit_type_from_name(u.type).id for u in radars]) + " ]"
tgt_info.name = (
g.obj_name
+ " ["
+ ",".join(
[db.unit_type_from_name(u.type).id for u in radars]
)
+ " ]"
)
if len(tgt_info.name) > 25:
tgt_info.name = g.obj_name + " [" + str(len(radars)) + " units]"
tgt_info.name = (
g.obj_name + " [" + str(len(radars)) + " units]"
)
tgt_info.radars = radars
tgt_info.location = g
tgt_info.threat_range = threat_range
@@ -73,5 +85,3 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
i = add_model_item(i, model, tgt_info)
self.setModel(model)

View File

@@ -5,7 +5,6 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
class StrikeTargetInfo:
def __init__(self):
self.name = ""
self.location = None
@@ -14,17 +13,14 @@ class StrikeTargetInfo:
class QStrikeTargetSelectionComboBox(QFilteredComboBox):
def __init__(self, game: Game, parent=None):
super(QStrikeTargetSelectionComboBox, self).__init__(parent)
self.game = game
self.find_possible_strike_targets()
for t in self.targets:
print(t.name + " - " + str(len(t.units)) + " " + str(len(t.buildings)))
def get_selected_target(self) -> StrikeTargetInfo:
n = self.currentText()
for target in self.targets:
@@ -44,12 +40,14 @@ class QStrikeTargetSelectionComboBox(QFilteredComboBox):
return i + 1
for cp in self.game.theater.controlpoints:
if cp.captured: continue
if cp.captured:
continue
added_obj_names = []
for g in cp.ground_objects:
if g.obj_name in added_obj_names: continue
if g.obj_name in added_obj_names:
continue
target = StrikeTargetInfo()
target.location = g
@@ -70,5 +68,3 @@ class QStrikeTargetSelectionComboBox(QFilteredComboBox):
added_obj_names.append(g.obj_name)
self.setModel(model)

View File

@@ -4,9 +4,12 @@ from PySide2.QtWidgets import QSpinBox
class TenthsSpinner(QSpinBox):
def __init__(self, minimum: Optional[int] = None,
maximum: Optional[int] = None,
initial: Optional[int] = None) -> None:
def __init__(
self,
minimum: Optional[int] = None,
maximum: Optional[int] = None,
initial: Optional[int] = None,
) -> None:
super().__init__()
if minimum is not None:

View File

@@ -27,8 +27,15 @@ class QFrontLine(QGraphicsLineItem):
change the mouse cursor on hover.
"""
def __init__(self, x1: float, y1: float, x2: float, y2: float,
mission_target: FrontLine, game_model: GameModel) -> None:
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
@@ -98,10 +105,9 @@ class QFrontLine(QGraphicsLineItem):
self.mission_target.control_point_b.base.affect_strength(-0.1)
self.game_model.game.initialize_turn()
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
def cheat_backward(self) -> None:
self.mission_target.control_point_a.base.affect_strength(-0.1)
self.mission_target.control_point_b.base.affect_strength(0.1)
self.game_model.game.initialize_turn()
GameUpdateSignal.get_instance().updateGame(self.game_model.game)

View File

@@ -70,10 +70,10 @@ from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
MAX_SHIP_DISTANCE = nautical_miles(80)
def binomial(i: int, n: int) -> float:
"""Binomial coefficient"""
return math.factorial(n) / float(
math.factorial(i) * math.factorial(n - i))
return math.factorial(n) / float(math.factorial(i) * math.factorial(n - i))
def bernstein(t: float, i: int, n: int) -> float:
@@ -92,7 +92,9 @@ def bezier(t: float, points: Iterable[Tuple[float, float]]) -> Tuple[float, floa
return x, y
def bezier_curve_range(n: int, points: Iterable[Tuple[float, float]]) -> Iterator[Tuple[float, float]]:
def bezier_curve_range(
n: int, points: Iterable[Tuple[float, float]]
) -> Iterator[Tuple[float, float]]:
"""Range of points in a curve bezier"""
for i in range(n):
t = i / float(n - 1)
@@ -133,7 +135,9 @@ class QLiberationMap(QGraphicsView):
self.setGame(game_model.game)
# Object displayed when unit is selected
self.movement_line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(QPointF(0,0),QPointF(0,0)))
self.movement_line = QtWidgets.QGraphicsLineItem(
QtCore.QLineF(QPointF(0, 0), QPointF(0, 0))
)
self.movement_line.setPen(QPen(CONST.COLORS["orange"], width=10.0))
self.selected_cp: QMapControlPoint = None
@@ -162,8 +166,7 @@ class QLiberationMap(QGraphicsView):
# update_flight_selection will be called in when the last
# package is removed. If no flight is selected, it's not a
# problem to also have no package selected.
logging.error(
"Flight was selected with no package selected")
logging.error("Flight was selected with no package selected")
return
# Optional[int] isn't a valid type for a Qt signal. None will be
@@ -183,7 +186,6 @@ class QLiberationMap(QGraphicsView):
self.navmesh_highlight: Optional[QPolygonF] = None
self.shortest_path_segments: List[QLineF] = []
def init_scene(self):
scene = QLiberationScene(self)
self.setScene(scene)
@@ -203,6 +205,7 @@ class QLiberationMap(QGraphicsView):
"""
Uncomment to set up theather reference points"""
def keyPressEvent(self, event):
modifiers = QtWidgets.QApplication.keyboardModifiers()
if not self.reference_point_setup_mode:
@@ -225,40 +228,39 @@ class QLiberationMap(QGraphicsView):
if event.key() == QtCore.Qt.Key_Down:
self.update_reference_point(
self.game.theater.reference_points[0],
Point(0, distance))
self.game.theater.reference_points[0], Point(0, distance)
)
if event.key() == QtCore.Qt.Key_Up:
self.update_reference_point(
self.game.theater.reference_points[0],
Point(0, -distance))
self.game.theater.reference_points[0], Point(0, -distance)
)
if event.key() == QtCore.Qt.Key_Left:
self.update_reference_point(
self.game.theater.reference_points[0],
Point(-distance, 0))
self.game.theater.reference_points[0], Point(-distance, 0)
)
if event.key() == QtCore.Qt.Key_Right:
self.update_reference_point(
self.game.theater.reference_points[0],
Point(distance, 0))
self.game.theater.reference_points[0], Point(distance, 0)
)
if event.key() == QtCore.Qt.Key_S:
self.update_reference_point(
self.game.theater.reference_points[1],
Point(0, distance))
self.game.theater.reference_points[1], Point(0, distance)
)
if event.key() == QtCore.Qt.Key_W:
self.update_reference_point(
self.game.theater.reference_points[1],
Point(0, -distance))
self.game.theater.reference_points[1], Point(0, -distance)
)
if event.key() == QtCore.Qt.Key_A:
self.update_reference_point(
self.game.theater.reference_points[1],
Point(-distance, 0))
self.game.theater.reference_points[1], Point(-distance, 0)
)
if event.key() == QtCore.Qt.Key_D:
self.update_reference_point(
self.game.theater.reference_points[1],
Point(distance, 0))
self.game.theater.reference_points[1], Point(distance, 0)
)
logging.debug(
f"Reference points: {self.game.theater.reference_points}")
logging.debug(f"Reference points: {self.game.theater.reference_points}")
self.reload_scene()
@staticmethod
@@ -275,16 +277,33 @@ class QLiberationMap(QGraphicsView):
distance_point = self._transform_point(culling_distance_point)
transformed = self._transform_point(point)
radius = distance_point[0] - transformed[0]
scene.addEllipse(transformed[0]-radius, transformed[1]-radius, 2*radius, 2*radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"])
scene.addEllipse(
transformed[0] - radius,
transformed[1] - radius,
2 * radius,
2 * radius,
CONST.COLORS["transparent"],
CONST.COLORS["light_green_transparent"],
)
for zone in culling_zones:
culling_distance_zone = Point(zone.x + culling_distance*1000, zone.y + culling_distance*1000)
culling_distance_zone = Point(
zone.x + culling_distance * 1000, zone.y + culling_distance * 1000
)
distance_zone = self._transform_point(culling_distance_zone)
transformed = self._transform_point(zone)
radius = distance_zone[0] - transformed[0]
scene.addEllipse(transformed[0]-radius, transformed[1]-radius, 2*radius, 2*radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"])
scene.addEllipse(
transformed[0] - radius,
transformed[1] - radius,
2 * radius,
2 * radius,
CONST.COLORS["transparent"],
CONST.COLORS["light_green_transparent"],
)
def draw_shapely_poly(self, scene: QGraphicsScene, poly: Polygon, pen: QPen,
brush: QBrush) -> Optional[QPolygonF]:
def draw_shapely_poly(
self, scene: QGraphicsScene, poly: Polygon, pen: QPen, brush: QBrush
) -> Optional[QPolygonF]:
if poly.is_empty:
return None
points = []
@@ -293,16 +312,18 @@ class QLiberationMap(QGraphicsView):
points.append(QPointF(x, y))
return scene.addPolygon(QPolygonF(points), pen, brush)
def draw_threat_zone(self, scene: QGraphicsScene, poly: Polygon,
player: bool) -> None:
def draw_threat_zone(
self, scene: QGraphicsScene, poly: Polygon, player: bool
) -> None:
if player:
brush = QColor(0, 132, 255, 100)
else:
brush = QColor(227, 32, 0, 100)
self.draw_shapely_poly(scene, poly, CONST.COLORS["transparent"], brush)
def display_threat_zones(self, scene: QGraphicsScene,
options: ThreatZoneOptions, player: bool) -> None:
def display_threat_zones(
self, scene: QGraphicsScene, options: ThreatZoneOptions, player: bool
) -> None:
"""Draws the threat zones on the map."""
threat_zones = self.game.threat_zone_for(player)
if options.all:
@@ -321,33 +342,44 @@ class QLiberationMap(QGraphicsView):
for poly in polys:
self.draw_threat_zone(scene, poly, player)
def draw_navmesh_neighbor_line(self, scene: QGraphicsScene, poly: Polygon,
begin: ShapelyPoint) -> None:
def draw_navmesh_neighbor_line(
self, scene: QGraphicsScene, poly: Polygon, begin: ShapelyPoint
) -> None:
vertex = Point(begin.x, begin.y)
centroid = poly.centroid
direction = Point(centroid.x, centroid.y)
end = vertex.point_from_heading(vertex.heading_between_point(direction),
nautical_miles(2).meters)
end = vertex.point_from_heading(
vertex.heading_between_point(direction), nautical_miles(2).meters
)
scene.addLine(QLineF(QPointF(*self._transform_point(vertex)),
QPointF(*self._transform_point(end))),
CONST.COLORS["yellow"])
scene.addLine(
QLineF(
QPointF(*self._transform_point(vertex)),
QPointF(*self._transform_point(end)),
),
CONST.COLORS["yellow"],
)
@singledispatchmethod
def draw_navmesh_border(self, intersection, scene: QGraphicsScene,
poly: Polygon) -> None:
raise NotImplementedError("draw_navmesh_border not implemented for %s",
intersection.__class__.__name__)
def draw_navmesh_border(
self, intersection, scene: QGraphicsScene, poly: Polygon
) -> None:
raise NotImplementedError(
"draw_navmesh_border not implemented for %s",
intersection.__class__.__name__,
)
@draw_navmesh_border.register
def draw_navmesh_point_border(self, intersection: ShapelyPoint,
scene: QGraphicsScene, poly: Polygon) -> None:
def draw_navmesh_point_border(
self, intersection: ShapelyPoint, scene: QGraphicsScene, poly: Polygon
) -> None:
# Draw a line from the vertex toward the center of the polygon.
self.draw_navmesh_neighbor_line(scene, poly, intersection)
@draw_navmesh_border.register
def draw_navmesh_edge_border(self, intersection: LineString,
scene: QGraphicsScene, poly: Polygon) -> None:
def draw_navmesh_edge_border(
self, intersection: LineString, scene: QGraphicsScene, poly: Polygon
) -> None:
# Draw a line from the center of the edge toward the center of the
# polygon.
edge_center = intersection.interpolate(0.5, normalized=True)
@@ -355,13 +387,16 @@ class QLiberationMap(QGraphicsView):
def display_navmesh(self, scene: QGraphicsScene, player: bool) -> None:
for navpoly in self.game.navmesh_for(player).polys:
self.draw_shapely_poly(scene, navpoly.poly, CONST.COLORS["black"],
CONST.COLORS["transparent"])
self.draw_shapely_poly(
scene, navpoly.poly, CONST.COLORS["black"], CONST.COLORS["transparent"]
)
position = self._transform_point(
Point(navpoly.poly.centroid.x, navpoly.poly.centroid.y))
text = scene.addSimpleText(f"Navmesh {navpoly.ident}",
self.waypoint_info_font)
Point(navpoly.poly.centroid.x, navpoly.poly.centroid.y)
)
text = scene.addSimpleText(
f"Navmesh {navpoly.ident}", self.waypoint_info_font
)
text.setBrush(QColor(255, 255, 255))
text.setPen(QColor(255, 255, 255))
text.moveBy(position[0] + 8, position[1])
@@ -370,8 +405,9 @@ class QLiberationMap(QGraphicsView):
for border in navpoly.neighbors.values():
self.draw_navmesh_border(border, scene, navpoly.poly)
def highlight_mouse_navmesh(self, scene: QGraphicsScene, navmesh: NavMesh,
mouse_position: Point) -> None:
def highlight_mouse_navmesh(
self, scene: QGraphicsScene, navmesh: NavMesh, mouse_position: Point
) -> None:
if self.navmesh_highlight is not None:
try:
scene.removeItem(self.navmesh_highlight)
@@ -381,11 +417,15 @@ class QLiberationMap(QGraphicsView):
if navpoly is None:
return
self.navmesh_highlight = self.draw_shapely_poly(
scene, navpoly.poly, CONST.COLORS["transparent"],
CONST.COLORS["light_green_transparent"])
scene,
navpoly.poly,
CONST.COLORS["transparent"],
CONST.COLORS["light_green_transparent"],
)
def draw_shortest_path(self, scene: QGraphicsScene, navmesh: NavMesh,
destination: Point, player: bool) -> None:
def draw_shortest_path(
self, scene: QGraphicsScene, navmesh: NavMesh, destination: Point, player: bool
) -> None:
for line in self.shortest_path_segments:
try:
scene.removeItem(line)
@@ -407,21 +447,36 @@ class QLiberationMap(QGraphicsView):
flight_path_pen = self.flight_path_pen(player, selected=True)
# Draw the line to the *middle* of the waypoint.
offset = self.WAYPOINT_SIZE // 2
self.shortest_path_segments.append(scene.addLine(
prev_pos[0] + offset, prev_pos[1] + offset,
new_pos[0] + offset, new_pos[1] + offset,
flight_path_pen
))
self.shortest_path_segments.append(
scene.addLine(
prev_pos[0] + offset,
prev_pos[1] + offset,
new_pos[0] + offset,
new_pos[1] + offset,
flight_path_pen,
)
)
self.shortest_path_segments.append(scene.addEllipse(
new_pos[0], new_pos[1], self.WAYPOINT_SIZE,
self.WAYPOINT_SIZE, flight_path_pen, flight_path_pen
))
self.shortest_path_segments.append(
scene.addEllipse(
new_pos[0],
new_pos[1],
self.WAYPOINT_SIZE,
self.WAYPOINT_SIZE,
flight_path_pen,
flight_path_pen,
)
)
prev_pos = new_pos
def draw_test_flight_plan(self, scene: QGraphicsScene, task: FlightType,
point_near_target: Point, player: bool) -> None:
def draw_test_flight_plan(
self,
scene: QGraphicsScene,
task: FlightType,
point_near_target: Point,
player: bool,
) -> None:
for line in self.shortest_path_segments:
try:
scene.removeItem(line)
@@ -438,8 +493,16 @@ class QLiberationMap(QGraphicsView):
origin = self.game.theater.enemy_points()[0]
package = Package(target)
flight = Flight(package, F_16C_50, 2, task, start_type="Warm",
departure=origin, arrival=origin, divert=None)
flight = Flight(
package,
F_16C_50,
2,
task,
start_type="Warm",
departure=origin,
arrival=origin,
divert=None,
)
package.add_flight(flight)
planner = FlightPlanBuilder(self.game, package, is_player=player)
try:
@@ -452,31 +515,49 @@ class QLiberationMap(QGraphicsView):
@staticmethod
def should_display_ground_objects_at(cp: ControlPoint) -> bool:
return ((DisplayOptions.sam_ranges and cp.captured) or
(DisplayOptions.enemy_sam_ranges and not cp.captured))
return (DisplayOptions.sam_ranges and cp.captured) or (
DisplayOptions.enemy_sam_ranges and not cp.captured
)
def draw_threat_range(self, scene: QGraphicsScene, group: Group, ground_object: TheaterGroundObject, cp: ControlPoint) -> None:
def draw_threat_range(
self,
scene: QGraphicsScene,
group: Group,
ground_object: TheaterGroundObject,
cp: ControlPoint,
) -> None:
go_pos = self._transform_point(ground_object.position)
detection_range = ground_object.detection_range(group)
threat_range = ground_object.threat_range(group)
if threat_range:
threat_pos = self._transform_point(
ground_object.position + Point(threat_range.meters,
threat_range.meters))
ground_object.position + Point(threat_range.meters, threat_range.meters)
)
threat_radius = Point(*go_pos).distance_to_point(Point(*threat_pos))
# Add threat range circle
scene.addEllipse(go_pos[0] - threat_radius / 2 + 7, go_pos[1] - threat_radius / 2 + 6,
threat_radius, threat_radius, self.threat_pen(cp.captured))
scene.addEllipse(
go_pos[0] - threat_radius / 2 + 7,
go_pos[1] - threat_radius / 2 + 6,
threat_radius,
threat_radius,
self.threat_pen(cp.captured),
)
if detection_range and DisplayOptions.detection_range:
# Add detection range circle
detection_pos = self._transform_point(
ground_object.position + Point(detection_range.meters,
detection_range.meters))
ground_object.position
+ Point(detection_range.meters, detection_range.meters)
)
detection_radius = Point(*go_pos).distance_to_point(Point(*detection_pos))
scene.addEllipse(go_pos[0] - detection_radius/2 + 7, go_pos[1] - detection_radius/2 + 6,
detection_radius, detection_radius, self.detection_pen(cp.captured))
scene.addEllipse(
go_pos[0] - detection_radius / 2 + 7,
go_pos[1] - detection_radius / 2 + 6,
detection_radius,
detection_radius,
self.detection_pen(cp.captured),
)
def draw_ground_objects(self, scene: QGraphicsScene, cp: ControlPoint) -> None:
added_objects = []
@@ -486,8 +567,22 @@ class QLiberationMap(QGraphicsView):
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], 14, 12, cp, ground_object, self.game, buildings))
buildings = self.game.theater.find_ground_objects_by_obj_name(
ground_object.obj_name
)
scene.addItem(
QMapGroundObject(
self,
go_pos[0],
go_pos[1],
14,
12,
cp,
ground_object,
self.game,
buildings,
)
)
should_display = self.should_display_ground_objects_at(cp)
if ground_object.might_have_aa and should_display:
@@ -508,10 +603,8 @@ class QLiberationMap(QGraphicsView):
if DisplayOptions.culling and self.game.settings.perf_culling:
self.display_culling(scene)
self.display_threat_zones(scene, DisplayOptions.blue_threat_zones,
player=True)
self.display_threat_zones(scene, DisplayOptions.red_threat_zones,
player=False)
self.display_threat_zones(scene, DisplayOptions.blue_threat_zones, player=True)
self.display_threat_zones(scene, DisplayOptions.red_threat_zones, player=False)
if DisplayOptions.navmeshes.blue_navmesh:
self.display_navmesh(scene, player=True)
@@ -522,24 +615,33 @@ class QLiberationMap(QGraphicsView):
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_model))
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_model,
)
)
if cp.captured:
pen = QPen(brush=CONST.COLORS[playerColor])
brush = CONST.COLORS[playerColor+"_transparent"]
brush = CONST.COLORS[playerColor + "_transparent"]
else:
pen = QPen(brush=CONST.COLORS[enemyColor])
brush = CONST.COLORS[enemyColor+"_transparent"]
brush = CONST.COLORS[enemyColor + "_transparent"]
self.draw_ground_objects(scene, cp)
if cp.target_position is not None:
proj = self._transform_point(cp.target_position)
scene.addLine(QLineF(QPointF(pos[0], pos[1]), QPointF(proj[0], proj[1])),
QPen(CONST.COLORS["green"], width=10, s=Qt.DashDotLine))
scene.addLine(
QLineF(QPointF(pos[0], pos[1]), QPointF(proj[0], proj[1])),
QPen(CONST.COLORS["green"], width=10, s=Qt.DashDotLine),
)
for cp in self.game.theater.enemy_points():
if DisplayOptions.lines:
@@ -585,8 +687,9 @@ class QLiberationMap(QGraphicsView):
continue
self.draw_flight_plan(scene, flight, selected)
def draw_flight_plan(self, scene: QGraphicsScene, flight: Flight,
selected: bool) -> None:
def draw_flight_plan(
self, scene: QGraphicsScene, flight: Flight, selected: bool
) -> None:
is_player = flight.from_cp.captured
pos = self._transform_point(flight.from_cp.position)
@@ -604,8 +707,7 @@ class QLiberationMap(QGraphicsView):
continue
new_pos = self._transform_point(Point(point.x, point.y))
self.draw_flight_path(scene, prev_pos, new_pos, is_player,
selected)
self.draw_flight_path(scene, prev_pos, new_pos, is_player, selected)
self.draw_waypoint(scene, new_pos, is_player, selected)
if selected and DisplayOptions.waypoint_info:
if point.waypoint_type in target_types:
@@ -613,44 +715,63 @@ class QLiberationMap(QGraphicsView):
# Don't draw dozens of targets over each other.
continue
drew_target = True
self.draw_waypoint_info(scene, idx + 1, point, new_pos,
flight.flight_plan)
self.draw_waypoint_info(
scene, idx + 1, point, new_pos, flight.flight_plan
)
prev_pos = tuple(new_pos)
if selected and DisplayOptions.barcap_commit_range:
self.draw_barcap_commit_range(scene, flight)
def draw_barcap_commit_range(self, scene: QGraphicsScene,
flight: Flight) -> None:
def draw_barcap_commit_range(self, scene: QGraphicsScene, flight: Flight) -> None:
if flight.flight_type is not FlightType.BARCAP:
return
if not isinstance(flight.flight_plan, BarCapFlightPlan):
return
start = flight.flight_plan.patrol_start
end = flight.flight_plan.patrol_end
line = LineString([
ShapelyPoint(start.x, start.y),
ShapelyPoint(end.x, end.y),
])
line = LineString(
[
ShapelyPoint(start.x, start.y),
ShapelyPoint(end.x, end.y),
]
)
doctrine = self.game.faction_for(flight.departure.captured).doctrine
bubble = line.buffer(doctrine.cap_engagement_range.meters)
self.flight_path_items.append(self.draw_shapely_poly(
scene, bubble, CONST.COLORS["yellow"], CONST.COLORS["transparent"]
))
self.flight_path_items.append(
self.draw_shapely_poly(
scene, bubble, CONST.COLORS["yellow"], CONST.COLORS["transparent"]
)
)
def draw_waypoint(self, scene: QGraphicsScene,
position: Tuple[float, float], player: bool,
selected: bool) -> None:
def draw_waypoint(
self,
scene: QGraphicsScene,
position: Tuple[float, float],
player: bool,
selected: bool,
) -> None:
waypoint_pen = self.waypoint_pen(player, selected)
waypoint_brush = self.waypoint_brush(player, selected)
self.flight_path_items.append(scene.addEllipse(
position[0], position[1], self.WAYPOINT_SIZE,
self.WAYPOINT_SIZE, waypoint_pen, waypoint_brush
))
self.flight_path_items.append(
scene.addEllipse(
position[0],
position[1],
self.WAYPOINT_SIZE,
self.WAYPOINT_SIZE,
waypoint_pen,
waypoint_brush,
)
)
def draw_waypoint_info(self, scene: QGraphicsScene, number: int,
waypoint: FlightWaypoint, position: Tuple[int, int],
flight_plan: FlightPlan) -> None:
def draw_waypoint_info(
self,
scene: QGraphicsScene,
number: int,
waypoint: FlightWaypoint,
position: Tuple[int, int],
flight_plan: FlightPlan,
) -> None:
altitude = int(waypoint.alt.feet)
altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL"
@@ -669,11 +790,13 @@ class QLiberationMap(QGraphicsView):
pen = QPen(QColor("black"), 0.3)
brush = QColor("white")
text = "\n".join([
f"{number} {waypoint.name}",
f"{altitude} ft {altitude_type}",
tot,
])
text = "\n".join(
[
f"{number} {waypoint.name}",
f"{altitude} ft {altitude_type}",
tot,
]
)
item = scene.addSimpleText(text, self.waypoint_info_font)
item.setFlag(QGraphicsItem.ItemIgnoresTransformations)
@@ -683,19 +806,30 @@ class QLiberationMap(QGraphicsView):
item.setZValue(2)
self.flight_path_items.append(item)
def draw_flight_path(self, scene: QGraphicsScene, pos0: Tuple[float, float],
pos1: Tuple[float, float], player: bool,
selected: bool) -> None:
def draw_flight_path(
self,
scene: QGraphicsScene,
pos0: Tuple[float, float],
pos1: Tuple[float, float],
player: bool,
selected: bool,
) -> None:
flight_path_pen = self.flight_path_pen(player, selected)
# Draw the line to the *middle* of the waypoint.
offset = self.WAYPOINT_SIZE // 2
self.flight_path_items.append(scene.addLine(
pos0[0] + offset, pos0[1] + offset,
pos1[0] + offset, pos1[1] + offset,
flight_path_pen
))
self.flight_path_items.append(
scene.addLine(
pos0[0] + offset,
pos0[1] + offset,
pos1[0] + offset,
pos1[1] + offset,
flight_path_pen,
)
)
def draw_bezier_frontline(self, scene: QGraphicsScene, pen:QPen, frontline: FrontLine) -> None:
def draw_bezier_frontline(
self, scene: QGraphicsScene, pen: QPen, frontline: FrontLine
) -> None:
"""
Thanks to Alquimista for sharing a python implementation of the bezier algorithm this is adapted from.
https://gist.github.com/Alquimista/1274149#file-bezdraw-py
@@ -706,7 +840,9 @@ class QLiberationMap(QGraphicsView):
bezier_fixed_points.append(self._transform_point(segment.point_b))
old_point = bezier_fixed_points[0]
for point in bezier_curve_range(int(len(bezier_fixed_points) * 2), bezier_fixed_points):
for point in bezier_curve_range(
int(len(bezier_fixed_points) * 2), bezier_fixed_points
):
scene.addLine(old_point[0], old_point[1], point[0], point[1], pen=pen)
old_point = point
@@ -715,14 +851,18 @@ class QLiberationMap(QGraphicsView):
for connected_cp in cp.connected_points:
pos2 = self._transform_point(connected_cp.position)
if not cp.captured:
color = CONST.COLORS["dark_"+enemyColor]
color = CONST.COLORS["dark_" + enemyColor]
else:
color = CONST.COLORS["dark_"+playerColor]
color = CONST.COLORS["dark_" + playerColor]
pen = QPen(brush=color)
pen.setColor(color)
pen.setWidth(6)
frontline = FrontLine(cp, connected_cp, self.game.theater)
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
if (
cp.captured
and not connected_cp.captured
and Conflict.has_frontline_between(cp, connected_cp)
):
if DisplayOptions.actual_frontline_pos:
self.draw_actual_frontline(frontline, scene, pen)
else:
@@ -730,35 +870,30 @@ class QLiberationMap(QGraphicsView):
else:
self.draw_bezier_frontline(scene, pen, frontline)
def draw_frontline_approximation(self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen) -> None:
def draw_frontline_approximation(
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen
) -> None:
posx = frontline.position
h = frontline.attack_heading
pos2 = self._transform_point(posx)
self.draw_bezier_frontline(scene, pen, frontline)
p1 = point_from_heading(pos2[0], pos2[1], h+180, 25)
p1 = point_from_heading(pos2[0], pos2[1], h + 180, 25)
p2 = point_from_heading(pos2[0], pos2[1], h, 25)
scene.addItem(
QFrontLine(
p1[0],
p1[1],
p2[0],
p2[1],
frontline,
self.game_model
)
QFrontLine(p1[0], p1[1], p2[0], p2[1], frontline, self.game_model)
)
def draw_actual_frontline(self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen) -> None:
def draw_actual_frontline(
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen
) -> None:
self.draw_bezier_frontline(scene, pen, frontline)
vector = Conflict.frontline_vector(
frontline.control_point_a,
frontline.control_point_b,
self.game.theater
frontline.control_point_a, frontline.control_point_b, self.game.theater
)
left_pos = self._transform_point(vector[0])
right_pos = self._transform_point(
vector[0].point_from_heading(vector[1], vector[2])
)
vector[0].point_from_heading(vector[1], vector[2])
)
scene.addItem(
QFrontLine(
left_pos[0],
@@ -766,7 +901,7 @@ class QLiberationMap(QGraphicsView):
right_pos[0],
right_pos[1],
frontline,
self.game_model
self.game_model,
)
)
@@ -779,26 +914,48 @@ class QLiberationMap(QGraphicsView):
SMALL_LINE = 2
dist = self.distance_to_pixels(nautical_miles(scale_distance_nm))
self.scene().addRect(POS_X, POS_Y-PADDING, PADDING*2 + dist, BIG_LINE*2+3*PADDING, pen=CONST.COLORS["black"], brush=CONST.COLORS["black"])
l = self.scene().addLine(POS_X + PADDING, POS_Y + BIG_LINE*2, POS_X + PADDING + dist, POS_Y + BIG_LINE*2)
self.scene().addRect(
POS_X,
POS_Y - PADDING,
PADDING * 2 + dist,
BIG_LINE * 2 + 3 * PADDING,
pen=CONST.COLORS["black"],
brush=CONST.COLORS["black"],
)
l = self.scene().addLine(
POS_X + PADDING,
POS_Y + BIG_LINE * 2,
POS_X + PADDING + dist,
POS_Y + BIG_LINE * 2,
)
text = self.scene().addText("0nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False))
text.setPos(POS_X, POS_Y + BIG_LINE*2)
text = self.scene().addText(
"0nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False)
)
text.setPos(POS_X, POS_Y + BIG_LINE * 2)
text.setDefaultTextColor(Qt.white)
text2 = self.scene().addText(str(scale_distance_nm) + "nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False))
text2 = self.scene().addText(
str(scale_distance_nm) + "nm",
font=QFont("Trebuchet MS", 6, weight=5, italic=False),
)
text2.setPos(POS_X + dist, POS_Y + BIG_LINE * 2)
text2.setDefaultTextColor(Qt.white)
l.setPen(CONST.COLORS["white"])
for i in range(number_of_points+1):
d = float(i)/float(number_of_points)
for i in range(number_of_points + 1):
d = float(i) / float(number_of_points)
if i == 0 or i == number_of_points:
h = BIG_LINE
else:
h = SMALL_LINE
l = self.scene().addLine(POS_X + PADDING + d * dist, POS_Y + BIG_LINE*2, POS_X + PADDING + d * dist, POS_Y + BIG_LINE - h)
l = self.scene().addLine(
POS_X + PADDING + d * dist,
POS_Y + BIG_LINE * 2,
POS_X + PADDING + d * dist,
POS_Y + BIG_LINE - h,
)
l.setPen(CONST.COLORS["white"])
def wheelEvent(self, event: QWheelEvent):
@@ -828,7 +985,8 @@ class QLiberationMap(QGraphicsView):
point_b = self.game.theater.reference_points[1]
world_distance = self._transpose_point(
point_b.world_coordinates - point_a.world_coordinates)
point_b.world_coordinates - point_a.world_coordinates
)
image_distance = point_b.image_coordinates - point_a.image_coordinates
x_scale = image_distance.x / world_distance.x
@@ -870,8 +1028,7 @@ class QLiberationMap(QGraphicsView):
scale = self._scaling_factor()
offset = point_a.image_coordinates - scene_point
scaled = self._transpose_point(
Point(offset.x / scale.x, offset.y / scale.y))
scaled = self._transpose_point(Point(offset.x / scale.x, offset.y / scale.y))
return point_a.world_coordinates - scaled
def distance_to_pixels(self, distance: Distance) -> int:
@@ -955,7 +1112,12 @@ class QLiberationMap(QGraphicsView):
for sea_zone in self.game.theater.landmap.sea_zones:
print(sea_zone)
poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone.exterior.coords])
poly = QPolygonF(
[
QPointF(*self._transform_point(Point(point[0], point[1])))
for point in sea_zone.exterior.coords
]
)
if self.reference_point_setup_mode:
color = "sea_blue_transparent"
else:
@@ -963,18 +1125,40 @@ class QLiberationMap(QGraphicsView):
scene.addPolygon(poly, CONST.COLORS[color], CONST.COLORS[color])
for inclusion_zone in self.game.theater.landmap.inclusion_zones:
poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone.exterior.coords])
poly = QPolygonF(
[
QPointF(*self._transform_point(Point(point[0], point[1])))
for point in inclusion_zone.exterior.coords
]
)
if self.reference_point_setup_mode:
scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_grey_transparent"])
scene.addPolygon(
poly,
CONST.COLORS["grey_transparent"],
CONST.COLORS["dark_grey_transparent"],
)
else:
scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"])
scene.addPolygon(
poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"]
)
for exclusion_zone in self.game.theater.landmap.exclusion_zones:
poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone.exterior.coords])
poly = QPolygonF(
[
QPointF(*self._transform_point(Point(point[0], point[1])))
for point in exclusion_zone.exterior.coords
]
)
if self.reference_point_setup_mode:
scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_dark_grey_transparent"])
scene.addPolygon(
poly,
CONST.COLORS["grey_transparent"],
CONST.COLORS["dark_dark_grey_transparent"],
)
else:
scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"])
scene.addPolygon(
poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"]
)
# Uncomment to display plan projection test
# self.projection_test()
@@ -983,15 +1167,18 @@ class QLiberationMap(QGraphicsView):
if self.reference_point_setup_mode:
for i, point in enumerate(self.game.theater.reference_points):
self.scene().addRect(
QRectF(point.image_coordinates.x, point.image_coordinates.y,
25, 25), pen=CONST.COLORS["red"],
brush=CONST.COLORS["red"])
QRectF(
point.image_coordinates.x, point.image_coordinates.y, 25, 25
),
pen=CONST.COLORS["red"],
brush=CONST.COLORS["red"],
)
text = self.scene().addText(
f"P{i} = {point.image_coordinates}",
font=QFont("Trebuchet MS", 14, weight=8, italic=False))
font=QFont("Trebuchet MS", 14, weight=8, italic=False),
)
text.setDefaultTextColor(CONST.COLORS["red"])
text.setPos(point.image_coordinates.x + 26,
point.image_coordinates.y)
text.setPos(point.image_coordinates.x + 26, point.image_coordinates.y)
# Set to True to visually debug _transform_point.
draw_transformed = False
@@ -1000,10 +1187,12 @@ class QLiberationMap(QGraphicsView):
self.scene().addRect(
QRectF(x, y, 25, 25),
pen=CONST.COLORS["red"],
brush=CONST.COLORS["red"])
brush=CONST.COLORS["red"],
)
text = self.scene().addText(
f"P{i}' = {x}, {y}",
font=QFont("Trebuchet MS", 14, weight=8, italic=False))
font=QFont("Trebuchet MS", 14, weight=8, italic=False),
)
text.setDefaultTextColor(CONST.COLORS["red"])
text.setPos(x + 26, y)
@@ -1023,7 +1212,9 @@ class QLiberationMap(QGraphicsView):
self.state = QLiberationMapState.MOVING_UNIT
self.selected_cp = selected_cp
position = self._transform_point(selected_cp.control_point.position)
self.movement_line = QtWidgets.QGraphicsLineItem(QLineF(QPointF(*position), QPointF(*position)))
self.movement_line = QtWidgets.QGraphicsLineItem(
QLineF(QPointF(*position), QPointF(*position))
)
self.scene().addItem(self.movement_line)
def is_valid_ship_pos(self, scene_position: Point) -> bool:
@@ -1043,7 +1234,8 @@ class QLiberationMap(QGraphicsView):
if self.state == QLiberationMapState.MOVING_UNIT:
self.setCursor(Qt.PointingHandCursor)
self.movement_line.setLine(
QLineF(self.movement_line.line().p1(), event.scenePos()))
QLineF(self.movement_line.line().p1(), event.scenePos())
)
if self.is_valid_ship_pos(mouse_position):
self.movement_line.setPen(CONST.COLORS["green"])
@@ -1053,21 +1245,28 @@ class QLiberationMap(QGraphicsView):
mouse_world_pos = self._scene_to_dcs_coords(mouse_position)
if DisplayOptions.navmeshes.blue_navmesh:
self.highlight_mouse_navmesh(
self.scene(), self.game.blue_navmesh,
self._scene_to_dcs_coords(mouse_position))
self.scene(),
self.game.blue_navmesh,
self._scene_to_dcs_coords(mouse_position),
)
if DisplayOptions.path_debug.shortest_path:
self.draw_shortest_path(self.scene(), self.game.blue_navmesh,
mouse_world_pos, player=True)
self.draw_shortest_path(
self.scene(), self.game.blue_navmesh, mouse_world_pos, player=True
)
if DisplayOptions.navmeshes.red_navmesh:
self.highlight_mouse_navmesh(
self.scene(), self.game.red_navmesh, mouse_world_pos)
self.scene(), self.game.red_navmesh, mouse_world_pos
)
debug_blue = DisplayOptions.path_debug_faction.blue
if DisplayOptions.path_debug.shortest_path:
self.draw_shortest_path(
self.scene(), self.game.navmesh_for(player=debug_blue),
mouse_world_pos, player=False)
self.scene(),
self.game.navmesh_for(player=debug_blue),
mouse_world_pos,
player=False,
)
elif not DisplayOptions.path_debug.hide:
if DisplayOptions.path_debug.barcap:
task = FlightType.BARCAP
@@ -1080,10 +1279,10 @@ class QLiberationMap(QGraphicsView):
elif DisplayOptions.path_debug.tarcap:
task = FlightType.TARCAP
else:
raise ValueError(
"Unexpected value for DisplayOptions.path_debug")
self.draw_test_flight_plan(self.scene(), task, mouse_world_pos,
player=debug_blue)
raise ValueError("Unexpected value for DisplayOptions.path_debug")
self.draw_test_flight_plan(
self.scene(), task, mouse_world_pos, player=debug_blue
)
def sceneMousePressEvent(self, event: QGraphicsSceneMouseEvent):
if self.state == QLiberationMapState.MOVING_UNIT:
@@ -1109,4 +1308,4 @@ class QLiberationMap(QGraphicsView):
self.scene().removeItem(self.movement_line)
except:
pass
self.selected_cp = None
self.selected_cp = None

View File

@@ -4,17 +4,18 @@ 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 = 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):
def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
super(QLiberationScene, self).mousePressEvent(event)
self.parent().sceneMousePressEvent(event)

View File

@@ -13,8 +13,16 @@ 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:
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
@@ -22,8 +30,7 @@ class QMapControlPoint(QMapObject):
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 = QAction(f"CHEAT: Capture {self.control_point.name}")
self.capture_action.triggered.connect(self.cheat_capture)
self.move_action = QAction("Move")
@@ -69,9 +76,7 @@ class QMapControlPoint(QMapObject):
def on_click(self) -> None:
self.base_details_dialog = QBaseMenu2(
self.window(),
self.control_point,
self.game_model
self.window(), self.control_point, self.game_model
)
self.base_details_dialog.show()
@@ -89,7 +94,10 @@ class QMapControlPoint(QMapObject):
return
for connected in self.control_point.connected_points:
if connected.captured and self.game_model.game.settings.enable_base_capture_cheat:
if (
connected.captured
and self.game_model.game.settings.enable_base_capture_cheat
):
menu.addAction(self.capture_action)
break
@@ -105,7 +113,7 @@ class QMapControlPoint(QMapObject):
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)

View File

@@ -9,17 +9,28 @@ 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 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:
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
@@ -42,7 +53,7 @@ class QMapGroundObject(QMapObject):
for g in self.ground_object.groups:
for u in g.units:
if u.type in units:
units[u.type] = units[u.type]+1
units[u.type] = units[u.type] + 1
else:
units[u.type] = 1
@@ -80,8 +91,12 @@ class QMapGroundObject(QMapObject):
if isinstance(self.ground_object, CoastalSiteGroundObject):
cat = "coastal"
rect = QRect(option.rect.x() + 2, option.rect.y(),
option.rect.width() - 2, option.rect.height())
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:
@@ -98,7 +113,7 @@ class QMapGroundObject(QMapObject):
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:
else:
painter.drawPixmap(rect, const.ICONS[cat + enemy_icons])
elif not is_dead:
if cat == "aa" and not has_threat:
@@ -130,13 +145,22 @@ class QMapGroundObject(QMapObject):
units_dead += len(g.units_losts)
if units_dead + units_alive > 0:
ratio = float(units_alive)/(float(units_dead) + float(units_alive))
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"]))
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(
@@ -144,6 +168,6 @@ class QMapGroundObject(QMapObject):
self.ground_object,
self.buildings,
self.control_point,
self.game
self.game,
)
self.ground_object_dialog.show()

View File

@@ -23,8 +23,9 @@ class QMapObject(QGraphicsRectItem):
change the mouse cursor on hover.
"""
def __init__(self, x: float, y: float, w: float, h: float,
mission_target: MissionTarget) -> None:
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

View File

@@ -5,8 +5,7 @@ from qt_ui.widgets.floatspinners import TenthsSpinner
class TenthsSpinSlider(QGridLayout):
def __init__(self, label: str, minimum: int, maximum: int,
initial: int) -> None:
def __init__(self, label: str, minimum: int, maximum: int, initial: int) -> None:
super().__init__()
self.addWidget(QLabel(label), 0, 0)

View File

@@ -31,12 +31,3 @@ class QSeadTargetInfoView(QGroupBox):
for r in self.sead_target_infos.radars:
radar_list_model.appendRow(QStandardItem(r.type))
self.radar_list.setModel(radar_list_model)

View File

@@ -1,7 +1,14 @@
import random
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QGroupBox, QLabel, QWidget, QVBoxLayout, QListView, QAbstractItemView
from PySide2.QtWidgets import (
QGroupBox,
QLabel,
QWidget,
QVBoxLayout,
QListView,
QAbstractItemView,
)
from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import StrikeTargetInfo
@@ -16,7 +23,9 @@ class QStrikeTargetInfoView(QGroupBox):
if strike_target_infos is None:
strike_target_infos = StrikeTargetInfo()
super(QStrikeTargetInfoView, self).__init__("Target : " + strike_target_infos.name)
super(QStrikeTargetInfoView, self).__init__(
"Target : " + strike_target_infos.name
)
self.strike_target_infos = strike_target_infos
@@ -24,14 +33,12 @@ class QStrikeTargetInfoView(QGroupBox):
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.addWidget(self.listView)
self.setLayout(layout)
def setTarget(self, target):
self.setTitle(target.name)
@@ -40,36 +47,27 @@ class QStrikeTargetInfoView(QGroupBox):
self.listView.setSelectionMode(QAbstractItemView.NoSelection)
if len(self.strike_target_infos.units) > 0:
dic = {}
for u in self.strike_target_infos.units:
if u.type in dic.keys():
dic[u.type] = dic[u.type] + 1
else:
dic[u.type] = 1
for k,v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
dic = {}
for u in self.strike_target_infos.units:
if u.type in dic.keys():
dic[u.type] = dic[u.type] + 1
else:
dic[u.type] = 1
for k, v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
else:
dic = {}
for b in self.strike_target_infos.buildings:
id = b.dcs_identifier
if b.is_dead:
id = id + "[Destroyed]"
if id in dic.keys():
dic[id] = dic[id] + 1
else:
dic[id] = 1
for k, v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
dic = {}
for b in self.strike_target_infos.buildings:
id = b.dcs_identifier
if b.is_dead:
id = id + "[Destroyed]"
if id in dic.keys():
dic[id] = dic[id] + 1
else:
dic[id] = 1
for k, v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
self.listView.setModel(model)