mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Added loadout editor to mission preparation screen.
This commit is contained in:
parent
48a40f2511
commit
9f319ab99a
@ -120,6 +120,10 @@ class AircraftConflictGenerator:
|
||||
else:
|
||||
group.units[idx].set_client()
|
||||
|
||||
# Do not generate player group with late activation.
|
||||
if group.late_activation:
|
||||
group.late_activation = False
|
||||
|
||||
# Set up F-14 Client to have pre-stored alignement
|
||||
if unit_type is F_14B:
|
||||
group.units[idx].set_property(F_14B.Properties.INSAlignmentStored.id, True)
|
||||
@ -284,20 +288,45 @@ class AircraftConflictGenerator:
|
||||
groups.append(group)
|
||||
return groups
|
||||
|
||||
def _setup_custom_payload(self, flight, group:FlyingGroup):
|
||||
if flight.use_custom_loadout:
|
||||
|
||||
logging.info("Custom loadout for flight : " + flight.__repr__())
|
||||
for p in group.units:
|
||||
p.pylons.clear()
|
||||
|
||||
for key in flight.loadout.keys():
|
||||
if "Pylon" + key in flight.unit_type.__dict__.keys():
|
||||
print(flight.loadout)
|
||||
weapon_dict = flight.unit_type.__dict__["Pylon" + key].__dict__
|
||||
if flight.loadout[key] in weapon_dict.keys():
|
||||
weapon = weapon_dict[flight.loadout[key]]
|
||||
group.load_pylon(weapon, int(key))
|
||||
else:
|
||||
logging.warning("Pylon not found ! => Pylon" + key + " on " + str(flight.unit_type))
|
||||
|
||||
|
||||
def generate_flights(self, cp, country, flight_planner:FlightPlanner):
|
||||
|
||||
for flight in flight_planner.interceptor_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_intercept_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
for flight in flight_planner.cap_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_cap_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
for flight in flight_planner.cas_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_cas_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
for flight in flight_planner.sead_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_sead_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
def generate_planned_flight(self, cp, country, flight:Flight):
|
||||
try:
|
||||
@ -321,10 +350,16 @@ class AircraftConflictGenerator:
|
||||
group.points[0].ETA = flight.scheduled_in * 60
|
||||
return group
|
||||
|
||||
def setup_group_as_intercept_flight(self, group, flight):
|
||||
group.points[0].ETA = 0
|
||||
group.late_activation = True
|
||||
self._setup_group(group, Intercept, flight.client_count)
|
||||
|
||||
|
||||
def setup_group_as_cap_flight(self, group, flight):
|
||||
self._setup_group(group, CAP, flight.client_count)
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point[0],point[1]), point[2])
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
def setup_group_as_cas_flight(self, group, flight):
|
||||
group.task = CAS.name
|
||||
@ -335,8 +370,8 @@ class AircraftConflictGenerator:
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
|
||||
for location in flight.points:
|
||||
group.add_waypoint(Point(location[0], location[1]), location[2])
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
def setup_group_as_sead_flight(self, group, flight):
|
||||
self._setup_group(group, SEAD, flight.client_count)
|
||||
@ -347,7 +382,7 @@ class AircraftConflictGenerator:
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.WeaponFree))
|
||||
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point[0], point[1]), point[2])
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
def generate_cas_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
@ -5,8 +5,7 @@ import random
|
||||
|
||||
from game import db
|
||||
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE
|
||||
from gen.flights.flight import Flight, FlightType
|
||||
|
||||
from gen.flights.flight import Flight, FlightType, FlightWaypoint
|
||||
|
||||
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
|
||||
STRIKE_MAX_RANGE = 150000
|
||||
@ -36,6 +35,7 @@ class FlightPlanner:
|
||||
|
||||
def __init__(self, from_cp, game):
|
||||
# TODO : have the flight planner depend on a 'stance' setting : [Defensive, Aggresive... etc] and faction doctrine
|
||||
# TODO : the flight planner should plan package and operations
|
||||
self.from_cp = from_cp
|
||||
self.game = game
|
||||
self.aircraft_inventory = {} # local copy of the airbase inventory
|
||||
@ -132,7 +132,9 @@ class FlightPlanner:
|
||||
patrolled = []
|
||||
for ground_object in self.from_cp.ground_objects:
|
||||
if ground_object.group_id not in patrolled and not ground_object.airbase_group:
|
||||
flight.points.append([ground_object.position.x, ground_object.position.y, patrol_alt])
|
||||
point = FlightWaypoint(ground_object.position.x, ground_object.position.y, patrol_alt)
|
||||
point.description = "Patrol #" + str(len(flight.points))
|
||||
flight.points.append(point)
|
||||
patrolled.append(ground_object.group_id)
|
||||
|
||||
self.cap_flights.append(flight)
|
||||
@ -170,7 +172,9 @@ class FlightPlanner:
|
||||
|
||||
location = random.choice(cas_location)
|
||||
flight.targets.append(cas_location)
|
||||
flight.points.append([location[0], location[1], 1000]) # TODO : Egress / Ingress points
|
||||
point = FlightWaypoint(location[0], location[1], 1000)
|
||||
point.description = "PROVIDE CAS"
|
||||
flight.points.append(point)
|
||||
|
||||
self.cas_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@ -203,13 +207,16 @@ class FlightPlanner:
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD]))
|
||||
|
||||
# Flight path : fly over each ground object (TODO : improve)
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||
|
||||
location = self.potential_sead_targets[0][0]
|
||||
self.potential_sead_targets.pop(0)
|
||||
flight.points.append([location.position.x, location.position.y, 1000]) # TODO : Egress / Ingress points
|
||||
|
||||
point = FlightWaypoint(location.position.x, location.position.y, 1000)
|
||||
point.description = "SEAD"
|
||||
point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
|
||||
self.cas_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
from dcs.unittype import UnitType
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
from dcs.unittype import UnitType
|
||||
|
||||
from game import db
|
||||
|
||||
|
||||
@ -26,36 +29,52 @@ class FlightType(Enum):
|
||||
EWAR = 16
|
||||
|
||||
|
||||
class Flight:
|
||||
class FlightWaypoint():
|
||||
|
||||
unit_type: UnitType
|
||||
def __init__(self, x: float, y: float, alt=0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.alt = alt
|
||||
self.name = ""
|
||||
self.description = ""
|
||||
self.targets = []
|
||||
|
||||
|
||||
class Flight:
|
||||
unit_type: UnitType = None
|
||||
from_cp = None
|
||||
points = []
|
||||
type = ""
|
||||
count = 0
|
||||
client_count = 0
|
||||
points: List[FlightWaypoint] = []
|
||||
flight_type: FlightType = None
|
||||
count: int = 0
|
||||
client_count: int = 0
|
||||
targets = []
|
||||
use_custom_loadout = False
|
||||
loadout = {}
|
||||
preset_loadout_name = ""
|
||||
|
||||
# How long before this flight should take off
|
||||
scheduled_in = 0
|
||||
|
||||
|
||||
def __init__(self, unit_type: UnitType, count: int, from_cp, flight_type: FlightType):
|
||||
self.unit_type = unit_type
|
||||
self.count = count
|
||||
self.from_cp = from_cp
|
||||
self.flight_type = flight_type
|
||||
self.points = []
|
||||
self.targets = []
|
||||
self.loadout = {}
|
||||
|
||||
def __repr__(self):
|
||||
return self.flight_type.name + " | " + str(self.count) + "x" + db.unit_type_name(self.unit_type)\
|
||||
return self.flight_type.name + " | " + str(self.count) + "x" + db.unit_type_name(self.unit_type) \
|
||||
+ " in " + str(self.scheduled_in) + " minutes (" + str(len(self.points)) + " wpt)"
|
||||
|
||||
|
||||
# Test
|
||||
if __name__ == '__main__':
|
||||
|
||||
from dcs.planes import A_10C
|
||||
from theater import ControlPoint, Point
|
||||
from theater import ControlPoint, Point, List
|
||||
|
||||
from_cp = ControlPoint(0, "AA", Point(0,0), None, [], 0, 0)
|
||||
from_cp = ControlPoint(0, "AA", Point(0, 0), None, [], 0, 0)
|
||||
f = Flight(A_10C, 4, from_cp, FlightType.CAS)
|
||||
f.scheduled_in = 50
|
||||
print(f)
|
||||
print(f)
|
||||
|
||||
@ -27,10 +27,6 @@ if __name__ == "__main__":
|
||||
logging.info("Using {} as userdata folder".format(persistency.base_path()))
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
uiconstants.load_icons()
|
||||
uiconstants.load_event_icons()
|
||||
uiconstants.load_aircraft_icons()
|
||||
uiconstants.load_vehicle_icons()
|
||||
|
||||
# Splash screen setup
|
||||
pixmap = QPixmap("./resources/ui/splash_screen.png")
|
||||
@ -38,6 +34,11 @@ if __name__ == "__main__":
|
||||
splash.show()
|
||||
|
||||
# Once splash screen is up : load resources & setup stuff
|
||||
uiconstants.load_icons()
|
||||
uiconstants.load_event_icons()
|
||||
uiconstants.load_aircraft_icons()
|
||||
uiconstants.load_vehicle_icons()
|
||||
|
||||
persistency.setup(sys.argv[1])
|
||||
|
||||
css = ""
|
||||
|
||||
@ -24,7 +24,6 @@ class QTopPanel(QFrame):
|
||||
self.turnCounter = QTurnCounter()
|
||||
self.budgetBox = QBudgetBox()
|
||||
|
||||
|
||||
self.passTurnButton = QPushButton("Pass Turn")
|
||||
self.passTurnButton.setIcon(CONST.ICONS["PassTurn"])
|
||||
self.passTurnButton.setProperty("style", "btn-primary")
|
||||
@ -34,6 +33,9 @@ class QTopPanel(QFrame):
|
||||
self.proceedButton.setIcon(CONST.ICONS["PassTurn"])
|
||||
self.proceedButton.setProperty("style", "btn-primary")
|
||||
self.proceedButton.clicked.connect(self.proceed)
|
||||
if self.game.turn == 0:
|
||||
self.proceedButton.setEnabled(False)
|
||||
|
||||
|
||||
self.submenus = QVBoxLayout()
|
||||
self.settings = QPushButton("Settings")
|
||||
@ -75,6 +77,7 @@ class QTopPanel(QFrame):
|
||||
def passTurn(self):
|
||||
self.game.pass_turn()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||
self.proceedButton.setEnabled(True)
|
||||
|
||||
def proceed(self):
|
||||
self.subwindow = QMissionPlanning(self.game)
|
||||
|
||||
@ -125,8 +125,8 @@ class QLiberationMap(QGraphicsView):
|
||||
for flight in planner.flights:
|
||||
scene.addEllipse(pos[0], pos[1], 4, 4)
|
||||
prev_pos = list(pos)
|
||||
for points in flight.points:
|
||||
new_pos = self._transform_point(Point(points[0], points[1]))
|
||||
for point in flight.points:
|
||||
new_pos = self._transform_point(Point(point.x, point.y))
|
||||
scene.addLine(prev_pos[0]+2, prev_pos[1]+2, new_pos[0]+2, new_pos[1]+2, flight_path_pen)
|
||||
scene.addEllipse(new_pos[0], new_pos[1], 4, 4, pen, brush)
|
||||
prev_pos = list(new_pos)
|
||||
|
||||
@ -26,9 +26,7 @@ class QChooseAirbase(QGroupBox):
|
||||
|
||||
def _on_airbase_selected(self):
|
||||
selected = self.depart_from.currentText()
|
||||
print("Airbase changed to : " + selected)
|
||||
self.selected_airbase_changed.emit(selected)
|
||||
print("Airbase changed to : " + selected)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ class QFlightPlanner(QTabWidget):
|
||||
def __init__(self, flight: Flight, game: Game):
|
||||
super(QFlightPlanner, self).__init__()
|
||||
self.general_settings_tab = QGeneralFlightSettingsTab(flight, game)
|
||||
self.payload_tab = QFlightPayloadTab(flight)
|
||||
self.payload_tab = QFlightPayloadTab(flight, game)
|
||||
self.waypoint_tab = QFlightWaypointTab(flight)
|
||||
self.addTab(self.general_settings_tab, "General Flight settings")
|
||||
self.addTab(self.payload_tab, "Payload")
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
from PySide2.QtWidgets import QFrame, QGridLayout, QLabel
|
||||
from PySide2.QtWidgets import QFrame, QGridLayout
|
||||
|
||||
from game import Game
|
||||
from gen.flights.flight import Flight
|
||||
from qt_ui.windows.mission.flight.payload.QLoadoutEditor import QLoadoutEditor
|
||||
|
||||
|
||||
class QFlightPayloadTab(QFrame):
|
||||
|
||||
def __init__(self, flight: Flight):
|
||||
def __init__(self, flight: Flight, game: Game):
|
||||
super(QFlightPayloadTab, self).__init__()
|
||||
self.flight = flight
|
||||
self.payload_editor = QLoadoutEditor(flight, game)
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QGridLayout()
|
||||
layout.addWidget(QLabel("Coming in two weeks"))
|
||||
layout.addWidget(self.payload_editor)
|
||||
self.setLayout(layout)
|
||||
|
||||
33
qt_ui/windows/mission/flight/payload/QLoadoutEditor.py
Normal file
33
qt_ui/windows/mission/flight/payload/QLoadoutEditor.py
Normal file
@ -0,0 +1,33 @@
|
||||
import inspect
|
||||
|
||||
from PySide2.QtWidgets import QLabel, QHBoxLayout, QGroupBox, QSpinBox, QGridLayout, QVBoxLayout, QSizePolicy
|
||||
|
||||
from qt_ui.windows.mission.flight.payload.QPylonEditor import QPylonEditor
|
||||
|
||||
|
||||
class QLoadoutEditor(QGroupBox):
|
||||
|
||||
def __init__(self, flight, game):
|
||||
super(QLoadoutEditor, self).__init__("Use custom loadout")
|
||||
self.flight = flight
|
||||
self.game = game
|
||||
self.setCheckable(True)
|
||||
self.setChecked(flight.use_custom_loadout)
|
||||
|
||||
self.toggled.connect(self.on_toggle)
|
||||
|
||||
layout = QGridLayout()
|
||||
|
||||
pylons = [v for v in self.flight.unit_type.__dict__.values() if inspect.isclass(v) and v.__name__.startswith("Pylon")]
|
||||
for i, pylon in enumerate(pylons):
|
||||
label = QLabel("<b>{}</b>".format(pylon.__name__[len("Pylon"):]))
|
||||
label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed))
|
||||
layout.addWidget(label, i, 0)
|
||||
layout.addWidget(QPylonEditor(flight, pylon, i+1), i, 1)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def on_toggle(self):
|
||||
self.flight.use_custom_loadout = self.isChecked()
|
||||
|
||||
|
||||
35
qt_ui/windows/mission/flight/payload/QPylonEditor.py
Normal file
35
qt_ui/windows/mission/flight/payload/QPylonEditor.py
Normal file
@ -0,0 +1,35 @@
|
||||
import logging
|
||||
|
||||
from PySide2.QtWidgets import QWidget, QSpinBox, QComboBox
|
||||
|
||||
|
||||
class QPylonEditor(QComboBox):
|
||||
|
||||
def __init__(self, flight, pylon, pylon_number):
|
||||
super(QPylonEditor, self).__init__()
|
||||
self.pylon = pylon
|
||||
self.pylon_number = pylon_number
|
||||
self.flight = flight
|
||||
|
||||
possible_loadout = [i for i in self.pylon.__dict__.keys() if i[:1] != '_']
|
||||
|
||||
if not str(self.pylon_number) in self.flight.loadout.keys():
|
||||
self.flight.loadout[str(self.pylon_number)] = ""
|
||||
|
||||
self.addItem("None")
|
||||
for i,k in enumerate(possible_loadout):
|
||||
self.addItem(str(k))
|
||||
if self.flight.loadout[str(self.pylon_number)] == str(k):
|
||||
self.setCurrentIndex(i + 1)
|
||||
|
||||
self.currentTextChanged.connect(self.on_pylon_change)
|
||||
|
||||
def on_pylon_change(self):
|
||||
selected = self.currentText()
|
||||
if selected == "None":
|
||||
logging.info("Pylon " + str(self.pylon_number) + " emptied")
|
||||
self.flight.loadout[str(self.pylon_number)] = ""
|
||||
else:
|
||||
logging.info("Pylon " + str(self.pylon_number) + " changed to " + selected)
|
||||
self.flight.loadout[str(self.pylon_number)] = selected
|
||||
|
||||
@ -2,11 +2,12 @@ from typing import List
|
||||
|
||||
from PySide2.QtGui import QStandardItem
|
||||
|
||||
from gen.flights.flight import FlightWaypoint
|
||||
|
||||
|
||||
class QWaypointItem(QStandardItem):
|
||||
|
||||
def __init__(self, point: List[int]):
|
||||
def __init__(self, point: FlightWaypoint):
|
||||
super(QWaypointItem, self).__init__()
|
||||
|
||||
self.setText("X: " + str(int(point[0])) + "; Y: " + str(int(point[1])) + "; Alt: " + str(int(point[2])) + "m")
|
||||
self.setText('{0: <16}'.format(point.description) + " -- [X: " + str(int(point.x)) + "; Y: " + str(int(point.y)) + "; Alt: " + str(int(point.alt)) + "m]")
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from PySide2.QtGui import QStandardItemModel
|
||||
from PySide2.QtWidgets import QListView
|
||||
|
||||
from gen.flights.flight import Flight
|
||||
from gen.flights.flight import Flight, FlightWaypoint
|
||||
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import QWaypointItem
|
||||
|
||||
|
||||
@ -13,6 +13,8 @@ class QFlightWaypointList(QListView):
|
||||
self.setModel(self.model)
|
||||
self.flight = flight
|
||||
|
||||
self.model.appendRow(QWaypointItem([flight.from_cp.position.x, flight.from_cp.position.y, 0]))
|
||||
takeoff = FlightWaypoint(flight.from_cp.position.x, flight.from_cp.position.y, 0)
|
||||
takeoff.description = "Take Off"
|
||||
self.model.appendRow(QWaypointItem(takeoff))
|
||||
for i, point in enumerate(self.flight.points):
|
||||
self.model.appendRow(QWaypointItem(point))
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -1,9 +1,8 @@
|
||||
import logging
|
||||
import typing
|
||||
import pickle
|
||||
import os
|
||||
import sys
|
||||
import pickle
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from dcs import installation
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user