mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Merge remote-tracking branch 'upstream/develop' into new-plugin-system
This commit is contained in:
commit
1bd26005f2
@ -71,7 +71,7 @@ class Weather:
|
|||||||
|
|
||||||
return WindConditions(
|
return WindConditions(
|
||||||
# Always some wind to make the smoke move a bit.
|
# Always some wind to make the smoke move a bit.
|
||||||
at_0m=Wind(wind_direction, min(1, base_wind * at_0m_factor)),
|
at_0m=Wind(wind_direction, max(1, base_wind * at_0m_factor)),
|
||||||
at_2000m=Wind(wind_direction, base_wind * at_2000m_factor),
|
at_2000m=Wind(wind_direction, base_wind * at_2000m_factor),
|
||||||
at_8000m=Wind(wind_direction, base_wind * at_8000m_factor)
|
at_8000m=Wind(wind_direction, base_wind * at_8000m_factor)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1140,8 +1140,9 @@ class HoldPointBuilder(PydcsWaypointBuilder):
|
|||||||
altitude=waypoint.alt,
|
altitude=waypoint.alt,
|
||||||
pattern=OrbitAction.OrbitPattern.Circle
|
pattern=OrbitAction.OrbitPattern.Circle
|
||||||
))
|
))
|
||||||
loiter.stop_after_time(
|
push_time = self.timing.push_time(self.flight, self.waypoint)
|
||||||
self.timing.push_time(self.flight, self.waypoint))
|
self.waypoint.departure_time = push_time
|
||||||
|
loiter.stop_after_time(push_time)
|
||||||
waypoint.add_task(loiter)
|
waypoint.add_task(loiter)
|
||||||
return waypoint
|
return waypoint
|
||||||
|
|
||||||
|
|||||||
@ -390,9 +390,6 @@ class CoalitionMissionPlanner:
|
|||||||
MAX_SEAD_RANGE = nm_to_meter(150)
|
MAX_SEAD_RANGE = nm_to_meter(150)
|
||||||
MAX_STRIKE_RANGE = nm_to_meter(150)
|
MAX_STRIKE_RANGE = nm_to_meter(150)
|
||||||
|
|
||||||
NON_CAP_MIN_DELAY = 1
|
|
||||||
NON_CAP_MAX_DELAY = 5
|
|
||||||
|
|
||||||
def __init__(self, game: Game, is_player: bool) -> None:
|
def __init__(self, game: Game, is_player: bool) -> None:
|
||||||
self.game = game
|
self.game = game
|
||||||
self.is_player = is_player
|
self.is_player = is_player
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, Iterable, Optional
|
from typing import Dict, Iterable, List, Optional
|
||||||
|
|
||||||
|
from dcs.mapping import Point
|
||||||
|
from dcs.point import MovingPoint, PointAction
|
||||||
|
from dcs.unittype import UnitType
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from dcs.unittype import UnitType
|
|
||||||
from dcs.point import MovingPoint, PointAction
|
|
||||||
from theater.controlpoint import ControlPoint, MissionTarget
|
from theater.controlpoint import ControlPoint, MissionTarget
|
||||||
|
|
||||||
|
|
||||||
@ -91,17 +93,22 @@ class FlightWaypoint:
|
|||||||
self.only_for_player = False
|
self.only_for_player = False
|
||||||
self.data = None
|
self.data = None
|
||||||
|
|
||||||
# This is set very late by the air conflict generator (part of mission
|
# These are set very late by the air conflict generator (part of mission
|
||||||
# generation). We do it late so that we don't need to propagate changes
|
# generation). We do it late so that we don't need to propagate changes
|
||||||
# to waypoint times whenever the player alters the package TOT or the
|
# to waypoint times whenever the player alters the package TOT or the
|
||||||
# flight's offset in the UI.
|
# flight's offset in the UI.
|
||||||
self.tot: Optional[int] = None
|
self.tot: Optional[int] = None
|
||||||
|
self.departure_time: Optional[int] = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def position(self) -> Point:
|
||||||
|
return Point(self.x, self.y)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_pydcs(cls, point: MovingPoint,
|
def from_pydcs(cls, point: MovingPoint,
|
||||||
from_cp: ControlPoint) -> "FlightWaypoint":
|
from_cp: ControlPoint) -> "FlightWaypoint":
|
||||||
waypoint = FlightWaypoint(point.position.x, point.position.y,
|
waypoint = FlightWaypoint(FlightWaypointType.NAV, point.position.x,
|
||||||
point.alt)
|
point.position.y, point.alt)
|
||||||
waypoint.alt_type = point.alt_type
|
waypoint.alt_type = point.alt_type
|
||||||
# Other actions exist... but none of them *should* be the first
|
# Other actions exist... but none of them *should* be the first
|
||||||
# waypoint for a flight.
|
# waypoint for a flight.
|
||||||
@ -159,14 +166,3 @@ class Flight:
|
|||||||
if waypoint.waypoint_type in types:
|
if waypoint.waypoint_type in types:
|
||||||
return waypoint
|
return waypoint
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# Test
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from dcs.planes import A_10C
|
|
||||||
from theater import ControlPoint, Point, List
|
|
||||||
|
|
||||||
from_cp = ControlPoint(0, "AA", Point(0, 0), Point(0, 0), [], 0, 0)
|
|
||||||
f = Flight(A_10C(), 4, from_cp, FlightType.CAS, "Cold")
|
|
||||||
f.scheduled_in = 50
|
|
||||||
print(f)
|
|
||||||
|
|||||||
@ -187,6 +187,10 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
closest_cache = ObjectiveDistanceCache.get_closest_airfields(location)
|
closest_cache = ObjectiveDistanceCache.get_closest_airfields(location)
|
||||||
for airfield in closest_cache.closest_airfields:
|
for airfield in closest_cache.closest_airfields:
|
||||||
|
# If the mission is a BARCAP of an enemy airfield, find the *next*
|
||||||
|
# closest enemy airfield.
|
||||||
|
if airfield == self.package.target:
|
||||||
|
continue
|
||||||
if airfield.captured != self.is_player:
|
if airfield.captured != self.is_player:
|
||||||
closest_airfield = airfield
|
closest_airfield = airfield
|
||||||
break
|
break
|
||||||
@ -198,10 +202,19 @@ class FlightPlanBuilder:
|
|||||||
closest_airfield.position
|
closest_airfield.position
|
||||||
)
|
)
|
||||||
|
|
||||||
|
min_distance_from_enemy = nm_to_meter(20)
|
||||||
|
distance_to_airfield = int(closest_airfield.position.distance_to_point(
|
||||||
|
self.package.target.position
|
||||||
|
))
|
||||||
|
distance_to_no_fly = distance_to_airfield - min_distance_from_enemy
|
||||||
|
min_cap_distance = min(self.doctrine.cap_min_distance_from_cp,
|
||||||
|
distance_to_no_fly)
|
||||||
|
max_cap_distance = min(self.doctrine.cap_max_distance_from_cp,
|
||||||
|
distance_to_no_fly)
|
||||||
|
|
||||||
end = location.position.point_from_heading(
|
end = location.position.point_from_heading(
|
||||||
heading,
|
heading,
|
||||||
random.randint(self.doctrine.cap_min_distance_from_cp,
|
random.randint(min_cap_distance, max_cap_distance)
|
||||||
self.doctrine.cap_max_distance_from_cp)
|
|
||||||
)
|
)
|
||||||
diameter = random.randint(
|
diameter = random.randint(
|
||||||
self.doctrine.cap_min_track_length,
|
self.doctrine.cap_min_track_length,
|
||||||
|
|||||||
@ -29,16 +29,19 @@ from pathlib import Path
|
|||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
from dcs.mapping import Point
|
||||||
from dcs.mission import Mission
|
from dcs.mission import Mission
|
||||||
from dcs.unittype import FlyingType
|
from dcs.unittype import FlyingType
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
|
from game.utils import meter_to_nm
|
||||||
from . import units
|
from . import units
|
||||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||||
from .airfields import RunwayData
|
from .airfields import RunwayData
|
||||||
from .airsupportgen import AwacsInfo, TankerInfo
|
from .airsupportgen import AwacsInfo, TankerInfo
|
||||||
from .briefinggen import CommInfo, JtacInfo, MissionInfoGenerator
|
from .briefinggen import CommInfo, JtacInfo, MissionInfoGenerator
|
||||||
from .flights.flight import FlightWaypoint, FlightWaypointType
|
from .flights.flight import FlightWaypoint, FlightWaypointType
|
||||||
|
from .flights.traveltime import TravelTime
|
||||||
from .radios import RadioFrequency
|
from .radios import RadioFrequency
|
||||||
|
|
||||||
|
|
||||||
@ -111,6 +114,7 @@ class FlightPlanBuilder:
|
|||||||
self.start_time = start_time
|
self.start_time = start_time
|
||||||
self.rows: List[List[str]] = []
|
self.rows: List[List[str]] = []
|
||||||
self.target_points: List[NumberedWaypoint] = []
|
self.target_points: List[NumberedWaypoint] = []
|
||||||
|
self.last_waypoint: Optional[FlightWaypoint] = None
|
||||||
|
|
||||||
def add_waypoint(self, waypoint_num: int, waypoint: FlightWaypoint) -> None:
|
def add_waypoint(self, waypoint_num: int, waypoint: FlightWaypoint) -> None:
|
||||||
if waypoint.waypoint_type == FlightWaypointType.TARGET_POINT:
|
if waypoint.waypoint_type == FlightWaypointType.TARGET_POINT:
|
||||||
@ -136,22 +140,59 @@ class FlightPlanBuilder:
|
|||||||
f"{first_waypoint_num}-{last_waypoint_num}",
|
f"{first_waypoint_num}-{last_waypoint_num}",
|
||||||
"Target points",
|
"Target points",
|
||||||
"0",
|
"0",
|
||||||
|
self._waypoint_distance(self.target_points[0].waypoint),
|
||||||
|
self._ground_speed(self.target_points[0].waypoint),
|
||||||
self._format_time(self.target_points[0].waypoint.tot),
|
self._format_time(self.target_points[0].waypoint.tot),
|
||||||
|
self._format_time(self.target_points[0].waypoint.departure_time),
|
||||||
])
|
])
|
||||||
|
self.last_waypoint = self.target_points[-1].waypoint
|
||||||
|
|
||||||
def add_waypoint_row(self, waypoint: NumberedWaypoint) -> None:
|
def add_waypoint_row(self, waypoint: NumberedWaypoint) -> None:
|
||||||
self.rows.append([
|
self.rows.append([
|
||||||
str(waypoint.number),
|
str(waypoint.number),
|
||||||
waypoint.waypoint.pretty_name,
|
waypoint.waypoint.pretty_name,
|
||||||
str(int(units.meters_to_feet(waypoint.waypoint.alt))),
|
str(int(units.meters_to_feet(waypoint.waypoint.alt))),
|
||||||
|
self._waypoint_distance(waypoint.waypoint),
|
||||||
|
self._ground_speed(waypoint.waypoint),
|
||||||
self._format_time(waypoint.waypoint.tot),
|
self._format_time(waypoint.waypoint.tot),
|
||||||
|
self._format_time(waypoint.waypoint.departure_time),
|
||||||
])
|
])
|
||||||
|
self.last_waypoint = waypoint.waypoint
|
||||||
|
|
||||||
def _format_time(self, time: Optional[int]) -> str:
|
def _format_time(self, time: Optional[int]) -> str:
|
||||||
if time is None:
|
if time is None:
|
||||||
return ""
|
return ""
|
||||||
local_time = self.start_time + datetime.timedelta(seconds=time)
|
local_time = self.start_time + datetime.timedelta(seconds=time)
|
||||||
return local_time.strftime(f"%H:%M:%S LOCAL")
|
return local_time.strftime(f"%H:%M:%S")
|
||||||
|
|
||||||
|
def _waypoint_distance(self, waypoint: FlightWaypoint) -> str:
|
||||||
|
if self.last_waypoint is None:
|
||||||
|
return "-"
|
||||||
|
|
||||||
|
distance = meter_to_nm(self.last_waypoint.position.distance_to_point(
|
||||||
|
waypoint.position
|
||||||
|
))
|
||||||
|
return f"{distance} NM"
|
||||||
|
|
||||||
|
def _ground_speed(self, waypoint: FlightWaypoint) -> str:
|
||||||
|
if self.last_waypoint is None:
|
||||||
|
return "-"
|
||||||
|
|
||||||
|
if waypoint.tot is None:
|
||||||
|
return "-"
|
||||||
|
|
||||||
|
if self.last_waypoint.departure_time is not None:
|
||||||
|
last_time = self.last_waypoint.departure_time
|
||||||
|
elif self.last_waypoint.tot is not None:
|
||||||
|
last_time = self.last_waypoint.tot
|
||||||
|
else:
|
||||||
|
return "-"
|
||||||
|
|
||||||
|
distance = meter_to_nm(self.last_waypoint.position.distance_to_point(
|
||||||
|
waypoint.position
|
||||||
|
))
|
||||||
|
duration = (waypoint.tot - last_time) / 3600
|
||||||
|
return f"{int(distance / duration)} kt"
|
||||||
|
|
||||||
def build(self) -> List[List[str]]:
|
def build(self) -> List[List[str]]:
|
||||||
return self.rows
|
return self.rows
|
||||||
@ -186,8 +227,9 @@ class BriefingPage(KneeboardPage):
|
|||||||
flight_plan_builder = FlightPlanBuilder(self.start_time)
|
flight_plan_builder = FlightPlanBuilder(self.start_time)
|
||||||
for num, waypoint in enumerate(self.flight.waypoints):
|
for num, waypoint in enumerate(self.flight.waypoints):
|
||||||
flight_plan_builder.add_waypoint(num, waypoint)
|
flight_plan_builder.add_waypoint(num, waypoint)
|
||||||
writer.table(flight_plan_builder.build(),
|
writer.table(flight_plan_builder.build(), headers=[
|
||||||
headers=["STPT", "Action", "Alt", "TOT"])
|
"#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"
|
||||||
|
])
|
||||||
|
|
||||||
writer.heading("Comm Ladder")
|
writer.heading("Comm Ladder")
|
||||||
comms = []
|
comms = []
|
||||||
|
|||||||
@ -337,6 +337,7 @@ class QPackageList(QListView):
|
|||||||
self.setItemDelegate(PackageDelegate())
|
self.setItemDelegate(PackageDelegate())
|
||||||
self.setIconSize(QSize(91, 24))
|
self.setIconSize(QSize(91, 24))
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectItems)
|
self.setSelectionBehavior(QAbstractItemView.SelectItems)
|
||||||
|
self.model().rowsInserted.connect(self.on_new_packages)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selected_item(self) -> Optional[Package]:
|
def selected_item(self) -> Optional[Package]:
|
||||||
@ -346,6 +347,14 @@ class QPackageList(QListView):
|
|||||||
return None
|
return None
|
||||||
return self.ato_model.package_at_index(index)
|
return self.ato_model.package_at_index(index)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
class QPackagePanel(QGroupBox):
|
class QPackagePanel(QGroupBox):
|
||||||
"""The package display portion of the ATO panel.
|
"""The package display portion of the ATO panel.
|
||||||
@ -357,7 +366,7 @@ class QPackagePanel(QGroupBox):
|
|||||||
def __init__(self, model: AtoModel) -> None:
|
def __init__(self, model: AtoModel) -> None:
|
||||||
super().__init__("Packages")
|
super().__init__("Packages")
|
||||||
self.ato_model = model
|
self.ato_model = model
|
||||||
self.ato_model.layoutChanged.connect(self.on_selection_changed)
|
self.ato_model.layoutChanged.connect(self.on_current_changed)
|
||||||
|
|
||||||
self.vbox = QVBoxLayout()
|
self.vbox = QVBoxLayout()
|
||||||
self.setLayout(self.vbox)
|
self.setLayout(self.vbox)
|
||||||
@ -378,15 +387,15 @@ class QPackagePanel(QGroupBox):
|
|||||||
self.delete_button.clicked.connect(self.on_delete)
|
self.delete_button.clicked.connect(self.on_delete)
|
||||||
self.button_row.addWidget(self.delete_button)
|
self.button_row.addWidget(self.delete_button)
|
||||||
|
|
||||||
self.selection_changed.connect(self.on_selection_changed)
|
self.current_changed.connect(self.on_current_changed)
|
||||||
self.on_selection_changed()
|
self.on_current_changed()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selection_changed(self):
|
def current_changed(self):
|
||||||
"""Returns the signal emitted when the flight selection changes."""
|
"""Returns the signal emitted when the flight selection changes."""
|
||||||
return self.package_list.selectionModel().selectionChanged
|
return self.package_list.selectionModel().currentChanged
|
||||||
|
|
||||||
def on_selection_changed(self) -> None:
|
def on_current_changed(self) -> None:
|
||||||
"""Updates the status of the edit and delete buttons."""
|
"""Updates the status of the edit and delete buttons."""
|
||||||
index = self.package_list.currentIndex()
|
index = self.package_list.currentIndex()
|
||||||
enabled = index.isValid()
|
enabled = index.isValid()
|
||||||
@ -436,8 +445,7 @@ class QAirTaskingOrderPanel(QSplitter):
|
|||||||
self.ato_model = game_model.ato_model
|
self.ato_model = game_model.ato_model
|
||||||
|
|
||||||
self.package_panel = QPackagePanel(self.ato_model)
|
self.package_panel = QPackagePanel(self.ato_model)
|
||||||
self.package_panel.selection_changed.connect(self.on_package_change)
|
self.package_panel.current_changed.connect(self.on_package_change)
|
||||||
self.ato_model.rowsInserted.connect(self.on_package_change)
|
|
||||||
self.addWidget(self.package_panel)
|
self.addWidget(self.package_panel)
|
||||||
|
|
||||||
self.flight_panel = QFlightPanel(game_model)
|
self.flight_panel = QFlightPanel(game_model)
|
||||||
|
|||||||
@ -39,3 +39,9 @@ class QOriginAirfieldSelector(QComboBox):
|
|||||||
self.addItem(f"{origin.name} ({available} available)", origin)
|
self.addItem(f"{origin.name} ({available} available)", origin)
|
||||||
self.model().sort(0)
|
self.model().sort(0)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> int:
|
||||||
|
origin = self.currentData()
|
||||||
|
inventory = self.global_inventory.for_control_point(origin)
|
||||||
|
return inventory.available(self.aircraft)
|
||||||
|
|||||||
@ -4,8 +4,16 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
from PySide2.QtCore import Qt, QPointF
|
from PySide2.QtCore import QPointF, Qt
|
||||||
from PySide2.QtGui import QBrush, QColor, QPen, QPixmap, QWheelEvent, QPolygonF
|
from PySide2.QtGui import (
|
||||||
|
QBrush,
|
||||||
|
QColor,
|
||||||
|
QFont,
|
||||||
|
QPen,
|
||||||
|
QPixmap,
|
||||||
|
QPolygonF,
|
||||||
|
QWheelEvent,
|
||||||
|
)
|
||||||
from PySide2.QtWidgets import (
|
from PySide2.QtWidgets import (
|
||||||
QFrame,
|
QFrame,
|
||||||
QGraphicsItem,
|
QGraphicsItem,
|
||||||
@ -46,6 +54,9 @@ class QLiberationMap(QGraphicsView):
|
|||||||
self.game_model = game_model
|
self.game_model = game_model
|
||||||
self.game: Optional[Game] = game_model.game
|
self.game: Optional[Game] = game_model.game
|
||||||
|
|
||||||
|
self.waypoint_info_font = QFont()
|
||||||
|
self.waypoint_info_font.setPointSize(12)
|
||||||
|
|
||||||
self.flight_path_items: List[QGraphicsItem] = []
|
self.flight_path_items: List[QGraphicsItem] = []
|
||||||
# A tuple of (package index, flight index), or none.
|
# A tuple of (package index, flight index), or none.
|
||||||
self.selected_flight: Optional[Tuple[int, int]] = None
|
self.selected_flight: Optional[Tuple[int, int]] = None
|
||||||
@ -345,19 +356,19 @@ class QLiberationMap(QGraphicsView):
|
|||||||
pen = QPen(QColor("black"), 0.3)
|
pen = QPen(QColor("black"), 0.3)
|
||||||
brush = QColor("white")
|
brush = QColor("white")
|
||||||
|
|
||||||
def draw_text(text: str, x: int, y: int) -> None:
|
text = "\n".join([
|
||||||
item = scene.addSimpleText(text)
|
f"{number} {waypoint.name}",
|
||||||
item.setBrush(brush)
|
f"{altitude} ft {altitude_type}",
|
||||||
item.setPen(pen)
|
tot,
|
||||||
item.moveBy(x, y)
|
])
|
||||||
item.setZValue(2)
|
|
||||||
self.flight_path_items.append(item)
|
|
||||||
|
|
||||||
draw_text(f"{number} {waypoint.name}", position[0] + 8,
|
item = scene.addSimpleText(text, self.waypoint_info_font)
|
||||||
position[1] - 15)
|
item.setFlag(QGraphicsItem.ItemIgnoresTransformations)
|
||||||
draw_text(f"{altitude} ft {altitude_type}", position[0] + 8,
|
item.setBrush(brush)
|
||||||
position[1] - 5)
|
item.setPen(pen)
|
||||||
draw_text(tot, position[0] + 8, position[1] + 5)
|
item.moveBy(position[0] + 8, position[1])
|
||||||
|
item.setZValue(2)
|
||||||
|
self.flight_path_items.append(item)
|
||||||
|
|
||||||
def draw_flight_path(self, scene: QGraphicsScene, pos0: Tuple[int, int],
|
def draw_flight_path(self, scene: QGraphicsScene, pos0: Tuple[int, int],
|
||||||
pos1: Tuple[int, int], player: bool,
|
pos1: Tuple[int, int], player: bool,
|
||||||
|
|||||||
@ -102,7 +102,7 @@ class QPackageDialog(QDialog):
|
|||||||
self.delete_flight_button = QPushButton("Delete Selected")
|
self.delete_flight_button = QPushButton("Delete Selected")
|
||||||
self.delete_flight_button.setProperty("style", "btn-danger")
|
self.delete_flight_button.setProperty("style", "btn-danger")
|
||||||
self.delete_flight_button.clicked.connect(self.on_delete_flight)
|
self.delete_flight_button.clicked.connect(self.on_delete_flight)
|
||||||
self.delete_flight_button.setEnabled(False)
|
self.delete_flight_button.setEnabled(model.rowCount() > 0)
|
||||||
self.button_layout.addWidget(self.delete_flight_button)
|
self.button_layout.addWidget(self.delete_flight_button)
|
||||||
|
|
||||||
self.button_layout.addStretch()
|
self.button_layout.addStretch()
|
||||||
@ -205,7 +205,7 @@ class QNewPackageDialog(QPackageDialog):
|
|||||||
def on_cancel(self) -> None:
|
def on_cancel(self) -> None:
|
||||||
super().on_cancel()
|
super().on_cancel()
|
||||||
for flight in self.package_model.package.flights:
|
for flight in self.package_model.package.flights:
|
||||||
self.game_model.game.aircraft_inventory.return_from_flight(flight)
|
self.game.aircraft_inventory.return_from_flight(flight)
|
||||||
|
|
||||||
|
|
||||||
class QEditPackageDialog(QPackageDialog):
|
class QEditPackageDialog(QPackageDialog):
|
||||||
|
|||||||
@ -53,10 +53,23 @@ class QFlightCreator(QDialog):
|
|||||||
[cp for cp in game.theater.controlpoints if cp.captured],
|
[cp for cp in game.theater.controlpoints if cp.captured],
|
||||||
self.aircraft_selector.currentData()
|
self.aircraft_selector.currentData()
|
||||||
)
|
)
|
||||||
|
self.aircraft_selector.currentIndexChanged.connect(self.update_max_size)
|
||||||
layout.addLayout(QLabeledWidget("Airfield:", self.airfield_selector))
|
layout.addLayout(QLabeledWidget("Airfield:", self.airfield_selector))
|
||||||
|
|
||||||
self.flight_size_spinner = QFlightSizeSpinner()
|
self.flight_size_spinner = QFlightSizeSpinner()
|
||||||
layout.addLayout(QLabeledWidget("Count:", self.flight_size_spinner))
|
self.update_max_size()
|
||||||
|
layout.addLayout(QLabeledWidget("Size:", self.flight_size_spinner))
|
||||||
|
|
||||||
|
self.client_slots_spinner = QFlightSizeSpinner(
|
||||||
|
min_size=0,
|
||||||
|
max_size=self.flight_size_spinner.value(),
|
||||||
|
default_size=0
|
||||||
|
)
|
||||||
|
self.flight_size_spinner.valueChanged.connect(
|
||||||
|
lambda v: self.client_slots_spinner.setMaximum(v)
|
||||||
|
)
|
||||||
|
layout.addLayout(
|
||||||
|
QLabeledWidget("Client Slots:", self.client_slots_spinner))
|
||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
|
|
||||||
@ -96,6 +109,7 @@ class QFlightCreator(QDialog):
|
|||||||
start_type = "Warm"
|
start_type = "Warm"
|
||||||
flight = Flight(aircraft, size, origin, task, start_type)
|
flight = Flight(aircraft, size, origin, task, start_type)
|
||||||
flight.scheduled_in = self.package.delay
|
flight.scheduled_in = self.package.delay
|
||||||
|
flight.client_count = self.client_slots_spinner.value()
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
self.created.emit(flight)
|
self.created.emit(flight)
|
||||||
@ -104,3 +118,8 @@ class QFlightCreator(QDialog):
|
|||||||
def on_aircraft_changed(self, index: int) -> None:
|
def on_aircraft_changed(self, index: int) -> None:
|
||||||
new_aircraft = self.aircraft_selector.itemData(index)
|
new_aircraft = self.aircraft_selector.itemData(index)
|
||||||
self.airfield_selector.change_aircraft(new_aircraft)
|
self.airfield_selector.change_aircraft(new_aircraft)
|
||||||
|
|
||||||
|
def update_max_size(self) -> None:
|
||||||
|
self.flight_size_spinner.setMaximum(
|
||||||
|
min(self.airfield_selector.available, 4)
|
||||||
|
)
|
||||||
|
|||||||
@ -39,46 +39,54 @@ write_state = function()
|
|||||||
-- messageAll("Done writing DCS Liberation state.")
|
-- messageAll("Done writing DCS Liberation state.")
|
||||||
end
|
end
|
||||||
|
|
||||||
debriefing_file_location = nil
|
|
||||||
if dcsLiberation then
|
local function discoverDebriefingFilePath()
|
||||||
debriefing_file_location = dcsLiberation.installPath
|
local function insertFileName(directoryOrFilePath, overrideFileName)
|
||||||
end
|
if overrideFileName then
|
||||||
if debriefing_file_location then
|
logger:info("Using LIBERATION_EXPORT_STAMPED_STATE to locate the state.json")
|
||||||
logger:info("Using DCS Liberation install folder for state.json")
|
return directoryOrFilePath .. os.time() .. "-state.json"
|
||||||
else
|
end
|
||||||
|
|
||||||
|
local filename = "state.json"
|
||||||
|
if not (directoryOrFilePath:sub(-#filename) == filename) then
|
||||||
|
return directoryOrFilePath .. filename
|
||||||
|
end
|
||||||
|
|
||||||
|
return directoryOrFilePath
|
||||||
|
end
|
||||||
|
|
||||||
|
-- establish a search pattern into the following modes
|
||||||
|
-- 1. Environment variable mode, to support dedicated server hosting
|
||||||
|
-- 2. Embedded DCS Liberation Generation, to support locally hosted single player
|
||||||
|
-- 3. Retain the classic TEMP directory logic
|
||||||
|
|
||||||
if os then
|
if os then
|
||||||
debriefing_file_location = os.getenv("LIBERATION_EXPORT_DIR")
|
local exportDirectory = os.getenv("LIBERATION_EXPORT_DIR")
|
||||||
if debriefing_file_location then debriefing_file_location = debriefing_file_location .. "\\" end
|
|
||||||
end
|
if exportDirectory then
|
||||||
if debriefing_file_location then
|
logger:info("Using LIBERATION_EXPORT_DIR to locate the state.json")
|
||||||
logger:info("Using LIBERATION_EXPORT_DIR environment variable for state.json")
|
local useCurrentStamping = os.getenv("LIBERATION_EXPORT_STAMPED_STATE")
|
||||||
else
|
exportDirectory = exportDirectory .. "\\"
|
||||||
if os then
|
return insertFileName(exportDirectory, useCurrentStamping)
|
||||||
debriefing_file_location = os.getenv("TEMP")
|
|
||||||
if debriefing_file_location then debriefing_file_location = debriefing_file_location .. "\\" end
|
|
||||||
end
|
|
||||||
if debriefing_file_location then
|
|
||||||
logger:info("Using TEMP environment variable for state.json")
|
|
||||||
else
|
|
||||||
if lfs then
|
|
||||||
debriefing_file_location = lfs.writedir()
|
|
||||||
end
|
|
||||||
if debriefing_file_location then
|
|
||||||
logger:info("Using DCS working directory for state.json")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
if debriefing_file_location then
|
if dcsLiberation then
|
||||||
local filename = "state.json"
|
logger:info("Using DCS Liberation install folder for state.json")
|
||||||
if not debriefing_file_location:sub(-#filename) == filename then
|
return insertFileName(dcsLiberation.installPath)
|
||||||
debriefing_file_location = debriefing_file_location .. filename
|
end
|
||||||
|
|
||||||
|
if lfs then
|
||||||
|
logger:info("Using DCS working directory for state.json")
|
||||||
|
return insertFileName(lfs.writedir())
|
||||||
end
|
end
|
||||||
logger:info(string.format("DCS Liberation state will be written as json to [[%s]]",debriefing_file_location))
|
|
||||||
else
|
|
||||||
logger:error("No usable storage path for state.json")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
debriefing_file_location = discoverDebriefingFilePath()
|
||||||
|
logger:info(string.format("DCS Liberation state will be written as json to [[%s]]",debriefing_file_location))
|
||||||
|
|
||||||
|
|
||||||
write_state_error_handling = function()
|
write_state_error_handling = function()
|
||||||
if pcall(write_state) then
|
if pcall(write_state) then
|
||||||
-- messageAll("Written DCS Liberation state to "..debriefing_file_location)
|
-- messageAll("Written DCS Liberation state to "..debriefing_file_location)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user