Feature/generator (#13)

* Create README.md

* Update README.md

* Update README.md

* Update README.md

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* added many ui options

* stable

* release candidate
This commit is contained in:
spencershepard
2022-01-30 23:36:14 -08:00
committed by GitHub
parent 07c4afa947
commit 8cda007768
45 changed files with 2321 additions and 8743 deletions

View File

@@ -0,0 +1,9 @@
You can add your own unit templates in this directory and they will appear in the mission generator.
1) Create an empty mission on Caucasus
2) Add ground unit groups.
3) Save the mission in this directory.
Tips:
-Drop your templates in the RotorOps Discord if you'd like to have them added in a release for everyone.
-The mission generator will only extract blue ground units from the template when selected from the "Blue Forces" menu, and vice versa.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,206 @@
import math
import sys
import os
import dcs
import RotorOpsMission as ROps
import RotorOpsUtils
import RotorOpsUnits
from PyQt5.QtWidgets import (
QApplication, QDialog, QMainWindow, QMessageBox
)
from PyQt5 import QtGui
from MissionGeneratorUI import Ui_MainWindow
scenarios = []
red_forces_files = []
blue_forces_files = []
class Window(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
print('running in a PyInstaller bundle')
home_dir = os.getcwd()
os.chdir(home_dir + "/Generator")
else:
print('running in a normal Python process')
self.m = ROps.RotorOpsMission()
self.setupUi(self)
self.connectSignalsSlots()
self.populateScenarios()
self.populateForces("red", self.redforces_comboBox, red_forces_files)
self.populateForces("blue", self.blueforces_comboBox, blue_forces_files)
self.populateSlotSelection()
self.background_label.setPixmap(QtGui.QPixmap(self.m.assets_dir + "/background.PNG"))
self.statusbar.setStyleSheet(
"QStatusBar{padding-left:5px;color:black;font-weight:bold;}")
def connectSignalsSlots(self):
# self.action_Exit.triggered.connect(self.close)
self.action_generateMission.triggered.connect(self.generateMissionAction)
self.action_scenarioSelected.triggered.connect(self.scenarioChanged)
def populateScenarios(self):
os.chdir(self.m.scenarios_dir)
path = os.getcwd()
dir_list = os.listdir(path)
print("Looking for mission files in '", path, "' :")
for filename in dir_list:
if filename.endswith(".miz"):
scenarios.append(filename)
self.scenario_comboBox.addItem(filename.removesuffix('.miz'))
def populateForces(self, side, combobox, files_list):
os.chdir(self.m.home_dir)
os.chdir(self.m.forces_dir + "/" + side)
path = os.getcwd()
dir_list = os.listdir(path)
print("Looking for " + side + " Forces files in '", os.getcwd(), "' :")
for filename in dir_list:
if filename.endswith(".miz"):
files_list.append(filename)
combobox.addItem(filename.removesuffix('.miz'))
def populateSlotSelection(self):
self.slot_template_comboBox.addItem("Multiple Slots")
for type in RotorOpsUnits.client_helos:
self.slot_template_comboBox.addItem(type.id)
def scenarioChanged(self):
try:
os.chdir(self.m.scenarios_dir)
filename = scenarios[self.scenario_comboBox.currentIndex()]
source_mission = dcs.mission.Mission()
source_mission.load_file(filename)
zones = source_mission.triggers.zones()
conflict_zones = 0
staging_zones = 0
conflict_zone_size_sum = 0
conflict_zone_distance_sum = 0
spawn_zones = 0
conflict_zone_positions = []
#friendly_airports = source_mission.getCoalitionAirports("blue")
#enemy_airports = source_mission.getCoalitionAirports("red")
friendly_airports = True
enemy_airports = True
## TODO: we should be creating a new instance of RotorOpsMission each time scenario is changed so we can access all methods and vars
for zone in zones:
if zone.name == "STAGING":
staging_zones += 1
if zone.name == "ALPHA" or zone.name == "BRAVO" or zone.name == "CHARLIE" or zone.name == "DELTA":
conflict_zones += 1
conflict_zone_size_sum += zone.radius
conflict_zone_positions.append(zone.position)
if zone.name.rfind("_SPAWN") > 0:
spawn_zones += 1
if conflict_zones > 1:
for index, position in enumerate(conflict_zone_positions):
if index > 0:
conflict_zone_distance_sum += RotorOpsUtils.getDistance(conflict_zone_positions[index], conflict_zone_positions[index - 1])
def validateTemplate():
valid = True
if len(staging_zones) < 1:
valid = False
if len(conflict_zones) < 1:
valid = False
if not friendly_airports:
valid = False
if not enemy_airports:
valid = False
return valid
if conflict_zones and staging_zones :
average_zone_size = conflict_zone_size_sum / conflict_zones
self.description_textBrowser.setText(
"Map: " + source_mission.terrain.name + "\n" +
"Conflict Zones: " + str(conflict_zones) + "\n" +
"Average Zone Size " + str(math.floor(average_zone_size)) + "m \n" +
"Infantry Spawn Zones: " + str(spawn_zones) + "\n" +
"Approx Distance: " + str(math.floor(RotorOpsUtils.convertMeterToNM(conflict_zone_distance_sum))) + "nm \n"
#"Validity Check:" + str(validateTemplate())
)
except:
self.description_textBrowser.setText("File error occured.")
def generateMissionAction(self):
red_forces_filename = red_forces_files[self.redforces_comboBox.currentIndex()]
blue_forces_filename = blue_forces_files[self.blueforces_comboBox.currentIndex()]
scenario_filename = scenarios[self.scenario_comboBox.currentIndex()]
data = {
"scenario_filename": scenario_filename,
"red_forces_filename": red_forces_filename,
"blue_forces_filename": blue_forces_filename,
"red_quantity": self.redqty_spinBox.value(),
"blue_quantity": self.blueqty_spinBox.value(),
"inf_spawn_qty": self.inf_spawn_spinBox.value(),
"apc_spawns_inf": self.apcs_spawn_checkBox.isChecked(),
"e_transport": self.enemy_transport_checkBox.isChecked(),
"e_attack_helos": self.enemy_attack_helos_checkBox.isChecked(),
"e_fighters": self.enemy_fighters_checkBox.isChecked(),
"e_attack_planes": self.enemy_attack_planes_checkBox.isChecked(),
"crates": self.logistics_crates_checkBox.isChecked(),
"f_awacs": self.awacs_checkBox.isChecked(),
"f_tankers": self.tankers_checkBox.isChecked(),
"smoke_zone": self.smoke_checkBox.isChecked(),
"voiceovers": self.voiceovers_checkBox.isChecked(),
"force_offroad": self.force_offroad_checkBox.isChecked(),
"game_display": self.game_status_checkBox.isChecked(),
"defending": self.defense_checkBox.isChecked(),
"slots": self.slot_template_comboBox.currentText(),
}
os.chdir(self.m.home_dir + '/Generator')
n = ROps.RotorOpsMission()
result = n.generateMission(data)
print("Generating mission with options:")
print(str(data))
# generate the mission
#result = self.m.generateMission(data)
#display results
if result["success"]:
print(result["filename"] + "' successfully generated in " + result["directory"])
self.statusbar.showMessage(result["filename"] + "' successfully generated in " + result["directory"], 10000)
msg = QMessageBox()
msg.setWindowTitle("Mission Generated")
msg.setText("Awesome, your mission is ready! It's located in this directory: \n" +
self.m.output_dir + "\n" +
"\n" +
"Next, you should use the DCS Mission Editor to fine tune unit placements. Don't be afraid to edit the missions that this generator produces. \n" +
"\n" +
"There are no hidden script changes, everything is visible in the ME. Triggers have been created to help you to add your own actions based on active zone and game status. \n" +
"\n" +
"Units can be changed or moved without issue. Player slots can be changed or moved without issue. \n" +
"\n" +
"Don't forget, you can also create your own templates that can include any mission options, objects, or even scripts. \n" +
"\n" +
"Have fun! \n"
)
x = msg.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())

View File

@@ -0,0 +1,41 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['MissionGenerator.py'],
pathex=['../'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='MissionGenerator',
icon='assets\\icon.ico',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None )

View File

@@ -0,0 +1,365 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'MissionGeneratorUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1140, 826)
font = QtGui.QFont()
font.setPointSize(10)
MainWindow.setFont(font)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("assets/icon.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
MainWindow.setWindowOpacity(4.0)
MainWindow.setAutoFillBackground(False)
MainWindow.setStyleSheet("background-color: white;")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.scenario_comboBox = QtWidgets.QComboBox(self.centralwidget)
self.scenario_comboBox.setGeometry(QtCore.QRect(270, 40, 361, 31))
self.scenario_comboBox.setToolTip("")
self.scenario_comboBox.setToolTipDuration(-1)
self.scenario_comboBox.setWhatsThis("")
self.scenario_comboBox.setObjectName("scenario_comboBox")
self.scenario_label = QtWidgets.QLabel(self.centralwidget)
self.scenario_label.setGeometry(QtCore.QRect(60, 30, 181, 41))
font = QtGui.QFont()
font.setPointSize(12)
self.scenario_label.setFont(font)
self.scenario_label.setObjectName("scenario_label")
self.generateButton = QtWidgets.QPushButton(self.centralwidget)
self.generateButton.setGeometry(QtCore.QRect(940, 720, 141, 41))
self.generateButton.setStyleSheet("background-color: white;\n"
"border-style: outset;\n"
"border-width: 2px;\n"
"border-radius: 15px;\n"
"border-color: black;\n"
"padding: 4px;")
self.generateButton.setObjectName("generateButton")
self.description_textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.description_textBrowser.setGeometry(QtCore.QRect(710, 20, 331, 131))
font = QtGui.QFont()
font.setPointSize(9)
self.description_textBrowser.setFont(font)
self.description_textBrowser.setStyleSheet("border-radius: 5px; color: gray")
self.description_textBrowser.setObjectName("description_textBrowser")
self.blueforces_comboBox = QtWidgets.QComboBox(self.centralwidget)
self.blueforces_comboBox.setGeometry(QtCore.QRect(790, 230, 291, 31))
self.blueforces_comboBox.setObjectName("blueforces_comboBox")
self.scenario_label_2 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_2.setGeometry(QtCore.QRect(690, 180, 141, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.scenario_label_2.setFont(font)
self.scenario_label_2.setObjectName("scenario_label_2")
self.scenario_label_3 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_3.setGeometry(QtCore.QRect(60, 180, 141, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.scenario_label_3.setFont(font)
self.scenario_label_3.setObjectName("scenario_label_3")
self.redforces_comboBox = QtWidgets.QComboBox(self.centralwidget)
self.redforces_comboBox.setGeometry(QtCore.QRect(170, 230, 291, 31))
self.redforces_comboBox.setObjectName("redforces_comboBox")
self.background_label = QtWidgets.QLabel(self.centralwidget)
self.background_label.setGeometry(QtCore.QRect(10, 430, 801, 371))
self.background_label.setAutoFillBackground(False)
self.background_label.setStyleSheet("")
self.background_label.setText("")
self.background_label.setPixmap(QtGui.QPixmap("assets/background.PNG"))
self.background_label.setObjectName("background_label")
self.scenario_hint_label = QtWidgets.QLabel(self.centralwidget)
self.scenario_hint_label.setGeometry(QtCore.QRect(250, 80, 381, 16))
self.scenario_hint_label.setAlignment(QtCore.Qt.AlignCenter)
self.scenario_hint_label.setObjectName("scenario_hint_label")
self.forces_hint_label = QtWidgets.QLabel(self.centralwidget)
self.forces_hint_label.setGeometry(QtCore.QRect(130, 270, 381, 16))
self.forces_hint_label.setAlignment(QtCore.Qt.AlignCenter)
self.forces_hint_label.setObjectName("forces_hint_label")
self.blueqty_spinBox = QtWidgets.QSpinBox(self.centralwidget)
self.blueqty_spinBox.setGeometry(QtCore.QRect(690, 230, 71, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.blueqty_spinBox.setFont(font)
self.blueqty_spinBox.setMinimum(0)
self.blueqty_spinBox.setMaximum(50)
self.blueqty_spinBox.setProperty("value", 3)
self.blueqty_spinBox.setObjectName("blueqty_spinBox")
self.redqty_spinBox = QtWidgets.QSpinBox(self.centralwidget)
self.redqty_spinBox.setGeometry(QtCore.QRect(70, 230, 71, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.redqty_spinBox.setFont(font)
self.redqty_spinBox.setMinimum(0)
self.redqty_spinBox.setMaximum(50)
self.redqty_spinBox.setProperty("value", 2)
self.redqty_spinBox.setObjectName("redqty_spinBox")
self.scenario_label_4 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_4.setGeometry(QtCore.QRect(670, 260, 101, 31))
font = QtGui.QFont()
font.setPointSize(8)
self.scenario_label_4.setFont(font)
self.scenario_label_4.setAlignment(QtCore.Qt.AlignCenter)
self.scenario_label_4.setObjectName("scenario_label_4")
self.game_status_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.game_status_checkBox.setGeometry(QtCore.QRect(910, 510, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.game_status_checkBox.setFont(font)
self.game_status_checkBox.setChecked(True)
self.game_status_checkBox.setTristate(False)
self.game_status_checkBox.setObjectName("game_status_checkBox")
self.voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.voiceovers_checkBox.setGeometry(QtCore.QRect(910, 570, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.voiceovers_checkBox.setFont(font)
self.voiceovers_checkBox.setChecked(True)
self.voiceovers_checkBox.setObjectName("voiceovers_checkBox")
self.logistics_crates_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.logistics_crates_checkBox.setGeometry(QtCore.QRect(910, 320, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.logistics_crates_checkBox.setFont(font)
self.logistics_crates_checkBox.setObjectName("logistics_crates_checkBox")
self.awacs_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.awacs_checkBox.setGeometry(QtCore.QRect(910, 350, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.awacs_checkBox.setFont(font)
self.awacs_checkBox.setStatusTip("")
self.awacs_checkBox.setObjectName("awacs_checkBox")
self.tankers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.tankers_checkBox.setGeometry(QtCore.QRect(910, 380, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.tankers_checkBox.setFont(font)
self.tankers_checkBox.setObjectName("tankers_checkBox")
self.apcs_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(470, 400, 251, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.apcs_spawn_checkBox.setFont(font)
self.apcs_spawn_checkBox.setObjectName("apcs_spawn_checkBox")
self.enemy_transport_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.enemy_transport_checkBox.setEnabled(False)
self.enemy_transport_checkBox.setGeometry(QtCore.QRect(70, 320, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.enemy_transport_checkBox.setFont(font)
self.enemy_transport_checkBox.setObjectName("enemy_transport_checkBox")
self.enemy_attack_helos_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.enemy_attack_helos_checkBox.setEnabled(True)
self.enemy_attack_helos_checkBox.setGeometry(QtCore.QRect(70, 350, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.enemy_attack_helos_checkBox.setFont(font)
self.enemy_attack_helos_checkBox.setObjectName("enemy_attack_helos_checkBox")
self.enemy_fighters_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.enemy_fighters_checkBox.setEnabled(False)
self.enemy_fighters_checkBox.setGeometry(QtCore.QRect(70, 380, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.enemy_fighters_checkBox.setFont(font)
self.enemy_fighters_checkBox.setObjectName("enemy_fighters_checkBox")
self.enemy_attack_planes_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.enemy_attack_planes_checkBox.setEnabled(True)
self.enemy_attack_planes_checkBox.setGeometry(QtCore.QRect(70, 410, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.enemy_attack_planes_checkBox.setFont(font)
self.enemy_attack_planes_checkBox.setObjectName("enemy_attack_planes_checkBox")
self.inf_spawn_spinBox = QtWidgets.QSpinBox(self.centralwidget)
self.inf_spawn_spinBox.setGeometry(QtCore.QRect(680, 360, 71, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.inf_spawn_spinBox.setFont(font)
self.inf_spawn_spinBox.setMinimum(0)
self.inf_spawn_spinBox.setMaximum(50)
self.inf_spawn_spinBox.setProperty("value", 2)
self.inf_spawn_spinBox.setObjectName("inf_spawn_spinBox")
self.smoke_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.smoke_checkBox.setGeometry(QtCore.QRect(910, 540, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.smoke_checkBox.setFont(font)
self.smoke_checkBox.setChecked(True)
self.smoke_checkBox.setObjectName("smoke_checkBox")
self.scenario_label_5 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_5.setGeometry(QtCore.QRect(50, 260, 101, 31))
font = QtGui.QFont()
font.setPointSize(8)
self.scenario_label_5.setFont(font)
self.scenario_label_5.setAlignment(QtCore.Qt.AlignCenter)
self.scenario_label_5.setObjectName("scenario_label_5")
self.forces_hint_label_2 = QtWidgets.QLabel(self.centralwidget)
self.forces_hint_label_2.setGeometry(QtCore.QRect(790, 270, 311, 20))
self.forces_hint_label_2.setAlignment(QtCore.Qt.AlignCenter)
self.forces_hint_label_2.setObjectName("forces_hint_label_2")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(470, 360, 191, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.label.setFont(font)
self.label.setObjectName("label")
self.slot_template_comboBox = QtWidgets.QComboBox(self.centralwidget)
self.slot_template_comboBox.setGeometry(QtCore.QRect(790, 630, 291, 31))
self.slot_template_comboBox.setObjectName("slot_template_comboBox")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(650, 630, 111, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.scenario_label_6 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_6.setGeometry(QtCore.QRect(470, 320, 141, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.scenario_label_6.setFont(font)
self.scenario_label_6.setObjectName("scenario_label_6")
self.force_offroad_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.force_offroad_checkBox.setGeometry(QtCore.QRect(910, 480, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.force_offroad_checkBox.setFont(font)
self.force_offroad_checkBox.setChecked(False)
self.force_offroad_checkBox.setTristate(False)
self.force_offroad_checkBox.setObjectName("force_offroad_checkBox")
self.defense_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.defense_checkBox.setGeometry(QtCore.QRect(60, 90, 181, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.defense_checkBox.setFont(font)
self.defense_checkBox.setObjectName("defense_checkBox")
self.background_label.raise_()
self.scenario_comboBox.raise_()
self.scenario_label.raise_()
self.generateButton.raise_()
self.description_textBrowser.raise_()
self.blueforces_comboBox.raise_()
self.scenario_label_2.raise_()
self.scenario_label_3.raise_()
self.redforces_comboBox.raise_()
self.scenario_hint_label.raise_()
self.forces_hint_label.raise_()
self.blueqty_spinBox.raise_()
self.redqty_spinBox.raise_()
self.scenario_label_4.raise_()
self.game_status_checkBox.raise_()
self.voiceovers_checkBox.raise_()
self.logistics_crates_checkBox.raise_()
self.awacs_checkBox.raise_()
self.tankers_checkBox.raise_()
self.apcs_spawn_checkBox.raise_()
self.enemy_transport_checkBox.raise_()
self.enemy_attack_helos_checkBox.raise_()
self.enemy_fighters_checkBox.raise_()
self.enemy_attack_planes_checkBox.raise_()
self.inf_spawn_spinBox.raise_()
self.smoke_checkBox.raise_()
self.scenario_label_5.raise_()
self.forces_hint_label_2.raise_()
self.label.raise_()
self.slot_template_comboBox.raise_()
self.label_2.raise_()
self.scenario_label_6.raise_()
self.force_offroad_checkBox.raise_()
self.defense_checkBox.raise_()
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1140, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setAcceptDrops(False)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.action_generateMission = QtWidgets.QAction(MainWindow)
self.action_generateMission.setObjectName("action_generateMission")
self.action_scenarioSelected = QtWidgets.QAction(MainWindow)
self.action_scenarioSelected.setObjectName("action_scenarioSelected")
self.action_blueforcesSelected = QtWidgets.QAction(MainWindow)
self.action_blueforcesSelected.setObjectName("action_blueforcesSelected")
self.action_redforcesSelected = QtWidgets.QAction(MainWindow)
self.action_redforcesSelected.setObjectName("action_redforcesSelected")
self.retranslateUi(MainWindow)
self.generateButton.clicked.connect(self.action_generateMission.trigger)
self.scenario_comboBox.currentIndexChanged['int'].connect(self.action_scenarioSelected.trigger)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "RotorOps Mission Generator"))
self.scenario_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own templates that include mission options like kneeboards, briefings, weather, static units, triggers, scripts, etc."))
self.scenario_label.setText(_translate("MainWindow", "Scenario Template:"))
self.generateButton.setText(_translate("MainWindow", "Generate Mission"))
self.description_textBrowser.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Provide close air support for our convoys as we take back Las Vegas from the enemy!</span></p></body></html>"))
self.blueforces_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own custom ground forces groups to be automatically generated."))
self.scenario_label_2.setText(_translate("MainWindow", "Friendly Forces:"))
self.scenario_label_3.setText(_translate("MainWindow", "Enemy Forces:"))
self.redforces_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own custom ground forces groups to be automatically generated."))
self.scenario_hint_label.setText(_translate("MainWindow", "Scenario templates are .miz files in \'Generator/Scenarios\'"))
self.forces_hint_label.setText(_translate("MainWindow", "Forces templates are .miz files in \'Generator/Forces\'"))
self.blueqty_spinBox.setStatusTip(_translate("MainWindow", "How many groups should we generate?"))
self.redqty_spinBox.setStatusTip(_translate("MainWindow", "How many groups should we generate?"))
self.scenario_label_4.setText(_translate("MainWindow", "Groups Per Zone"))
self.game_status_checkBox.setStatusTip(_translate("MainWindow", "Enable an onscreen zone status display. This helps keep focus on the active conflict zone."))
self.game_status_checkBox.setText(_translate("MainWindow", "Game Status Display"))
self.voiceovers_checkBox.setStatusTip(_translate("MainWindow", "Voiceovers from the ground commander. Helps keep focus on the active zone."))
self.voiceovers_checkBox.setText(_translate("MainWindow", "Voiceovers"))
self.logistics_crates_checkBox.setStatusTip(_translate("MainWindow", "Enable CTLD logistics crates for building ground units and air defenses."))
self.logistics_crates_checkBox.setText(_translate("MainWindow", "Logistics Crates"))
self.awacs_checkBox.setText(_translate("MainWindow", "Friendly AWACS"))
self.tankers_checkBox.setText(_translate("MainWindow", "Friendly Tankers"))
self.apcs_spawn_checkBox.setStatusTip(_translate("MainWindow", "Friendly/enemy APCs will drop infantry when reaching a new conflict zone."))
self.apcs_spawn_checkBox.setText(_translate("MainWindow", "APCs Spawn Infantry"))
self.enemy_transport_checkBox.setStatusTip(_translate("MainWindow", "Not yet implemented."))
self.enemy_transport_checkBox.setText(_translate("MainWindow", "Enemy Transport Helicopters"))
self.enemy_attack_helos_checkBox.setStatusTip(_translate("MainWindow", "Not yet implemented."))
self.enemy_attack_helos_checkBox.setText(_translate("MainWindow", "Enemy Attack Helicopters"))
self.enemy_fighters_checkBox.setStatusTip(_translate("MainWindow", "Not yet implemented."))
self.enemy_fighters_checkBox.setText(_translate("MainWindow", "Enemy Fighter Planes"))
self.enemy_attack_planes_checkBox.setStatusTip(_translate("MainWindow", "Not yet implemented."))
self.enemy_attack_planes_checkBox.setText(_translate("MainWindow", "Enemy Ground Attack Planes"))
self.inf_spawn_spinBox.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
self.smoke_checkBox.setStatusTip(_translate("MainWindow", "Not yet implemented."))
self.smoke_checkBox.setText(_translate("MainWindow", "Smoke Active Zone"))
self.scenario_label_5.setText(_translate("MainWindow", "Groups Per Zone"))
self.forces_hint_label_2.setText(_translate("MainWindow", "Forces templates are .miz files in \'Generator/Forces\'"))
self.label.setText(_translate("MainWindow", "Infantry Groups per zone:"))
self.slot_template_comboBox.setStatusTip(_translate("MainWindow", "Default player/client spawn locations at a friendly airport."))
self.label_2.setText(_translate("MainWindow", "Player Slots"))
self.scenario_label_6.setText(_translate("MainWindow", "Infantry Spawns:"))
self.force_offroad_checkBox.setStatusTip(_translate("MainWindow", "May help prevent long travel times or pathfinding issues. Tip: You can change this dynamically from mission triggers."))
self.force_offroad_checkBox.setText(_translate("MainWindow", "Force Offroad"))
self.defense_checkBox.setText(_translate("MainWindow", "Defensive Mode"))
self.action_generateMission.setText(_translate("MainWindow", "_generateMission"))
self.action_scenarioSelected.setText(_translate("MainWindow", "_scenarioSelected"))
self.action_blueforcesSelected.setText(_translate("MainWindow", "_blueforcesSelected"))
self.action_redforcesSelected.setText(_translate("MainWindow", "_redforcesSelected"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

View File

@@ -0,0 +1,846 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1140</width>
<height>826</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="windowTitle">
<string>RotorOps Mission Generator</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>assets/icon.ico</normaloff>assets/icon.ico</iconset>
</property>
<property name="windowOpacity">
<double>4.000000000000000</double>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color: white;</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QComboBox" name="scenario_comboBox">
<property name="geometry">
<rect>
<x>270</x>
<y>40</y>
<width>361</width>
<height>31</height>
</rect>
</property>
<property name="toolTip">
<string/>
</property>
<property name="toolTipDuration">
<number>-1</number>
</property>
<property name="statusTip">
<string>Tip: You can create your own templates that include mission options like kneeboards, briefings, weather, static units, triggers, scripts, etc.</string>
</property>
<property name="whatsThis">
<string/>
</property>
</widget>
<widget class="QLabel" name="scenario_label">
<property name="geometry">
<rect>
<x>60</x>
<y>30</y>
<width>181</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Scenario Template:</string>
</property>
</widget>
<widget class="QPushButton" name="generateButton">
<property name="geometry">
<rect>
<x>940</x>
<y>720</y>
<width>141</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: white;
border-style: outset;
border-width: 2px;
border-radius: 15px;
border-color: black;
padding: 4px;</string>
</property>
<property name="text">
<string>Generate Mission</string>
</property>
</widget>
<widget class="QTextBrowser" name="description_textBrowser">
<property name="geometry">
<rect>
<x>710</x>
<y>20</y>
<width>331</width>
<height>131</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">border-radius: 5px; color: gray</string>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Provide close air support for our convoys as we take back Las Vegas from the enemy!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QComboBox" name="blueforces_comboBox">
<property name="geometry">
<rect>
<x>790</x>
<y>230</y>
<width>291</width>
<height>31</height>
</rect>
</property>
<property name="statusTip">
<string>Tip: You can create your own custom ground forces groups to be automatically generated.</string>
</property>
</widget>
<widget class="QLabel" name="scenario_label_2">
<property name="geometry">
<rect>
<x>690</x>
<y>180</y>
<width>141</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Friendly Forces:</string>
</property>
</widget>
<widget class="QLabel" name="scenario_label_3">
<property name="geometry">
<rect>
<x>60</x>
<y>180</y>
<width>141</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Enemy Forces:</string>
</property>
</widget>
<widget class="QComboBox" name="redforces_comboBox">
<property name="geometry">
<rect>
<x>170</x>
<y>230</y>
<width>291</width>
<height>31</height>
</rect>
</property>
<property name="statusTip">
<string>Tip: You can create your own custom ground forces groups to be automatically generated.</string>
</property>
</widget>
<widget class="QLabel" name="background_label">
<property name="geometry">
<rect>
<x>10</x>
<y>430</y>
<width>801</width>
<height>371</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>assets/background.PNG</pixmap>
</property>
</widget>
<widget class="QLabel" name="scenario_hint_label">
<property name="geometry">
<rect>
<x>250</x>
<y>80</y>
<width>381</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Scenario templates are .miz files in 'Generator/Scenarios'</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="forces_hint_label">
<property name="geometry">
<rect>
<x>130</x>
<y>270</y>
<width>381</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Forces templates are .miz files in 'Generator/Forces'</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QSpinBox" name="blueqty_spinBox">
<property name="geometry">
<rect>
<x>690</x>
<y>230</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="statusTip">
<string>How many groups should we generate?</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
<widget class="QSpinBox" name="redqty_spinBox">
<property name="geometry">
<rect>
<x>70</x>
<y>230</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="statusTip">
<string>How many groups should we generate?</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
<widget class="QLabel" name="scenario_label_4">
<property name="geometry">
<rect>
<x>670</x>
<y>260</y>
<width>101</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>Groups Per Zone</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QCheckBox" name="game_status_checkBox">
<property name="geometry">
<rect>
<x>910</x>
<y>510</y>
<width>191</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Enable an onscreen zone status display. This helps keep focus on the active conflict zone.</string>
</property>
<property name="text">
<string>Game Status Display</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="tristate">
<bool>false</bool>
</property>
</widget>
<widget class="QCheckBox" name="voiceovers_checkBox">
<property name="geometry">
<rect>
<x>910</x>
<y>570</y>
<width>191</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Voiceovers from the ground commander. Helps keep focus on the active zone.</string>
</property>
<property name="text">
<string>Voiceovers</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QCheckBox" name="logistics_crates_checkBox">
<property name="geometry">
<rect>
<x>910</x>
<y>320</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="statusTip">
<string>Enable CTLD logistics crates for building ground units and air defenses.</string>
</property>
<property name="text">
<string>Logistics Crates</string>
</property>
</widget>
<widget class="QCheckBox" name="awacs_checkBox">
<property name="geometry">
<rect>
<x>910</x>
<y>350</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="statusTip">
<string/>
</property>
<property name="text">
<string>Friendly AWACS</string>
</property>
</widget>
<widget class="QCheckBox" name="tankers_checkBox">
<property name="geometry">
<rect>
<x>910</x>
<y>380</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Friendly Tankers</string>
</property>
</widget>
<widget class="QCheckBox" name="apcs_spawn_checkBox">
<property name="geometry">
<rect>
<x>470</x>
<y>400</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="statusTip">
<string>Friendly/enemy APCs will drop infantry when reaching a new conflict zone.</string>
</property>
<property name="text">
<string>APCs Spawn Infantry</string>
</property>
</widget>
<widget class="QCheckBox" name="enemy_transport_checkBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>70</x>
<y>320</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="statusTip">
<string>Not yet implemented.</string>
</property>
<property name="text">
<string>Enemy Transport Helicopters</string>
</property>
</widget>
<widget class="QCheckBox" name="enemy_attack_helos_checkBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>70</x>
<y>350</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="statusTip">
<string>Not yet implemented.</string>
</property>
<property name="text">
<string>Enemy Attack Helicopters</string>
</property>
</widget>
<widget class="QCheckBox" name="enemy_fighters_checkBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>70</x>
<y>380</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="statusTip">
<string>Not yet implemented.</string>
</property>
<property name="text">
<string>Enemy Fighter Planes</string>
</property>
</widget>
<widget class="QCheckBox" name="enemy_attack_planes_checkBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>70</x>
<y>410</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="statusTip">
<string>Not yet implemented.</string>
</property>
<property name="text">
<string>Enemy Ground Attack Planes</string>
</property>
</widget>
<widget class="QSpinBox" name="inf_spawn_spinBox">
<property name="geometry">
<rect>
<x>680</x>
<y>360</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="statusTip">
<string>This value is multiplied by the number of spawn zones in the mission template.</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
<widget class="QCheckBox" name="smoke_checkBox">
<property name="geometry">
<rect>
<x>910</x>
<y>540</y>
<width>191</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Not yet implemented.</string>
</property>
<property name="text">
<string>Smoke Active Zone</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="scenario_label_5">
<property name="geometry">
<rect>
<x>50</x>
<y>260</y>
<width>101</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>Groups Per Zone</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="forces_hint_label_2">
<property name="geometry">
<rect>
<x>790</x>
<y>270</y>
<width>311</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Forces templates are .miz files in 'Generator/Forces'</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>470</x>
<y>360</y>
<width>191</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Infantry Groups per zone:</string>
</property>
</widget>
<widget class="QComboBox" name="slot_template_comboBox">
<property name="geometry">
<rect>
<x>790</x>
<y>630</y>
<width>291</width>
<height>31</height>
</rect>
</property>
<property name="statusTip">
<string>Default player/client spawn locations at a friendly airport.</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>650</x>
<y>630</y>
<width>111</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Player Slots</string>
</property>
</widget>
<widget class="QLabel" name="scenario_label_6">
<property name="geometry">
<rect>
<x>470</x>
<y>320</y>
<width>141</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Infantry Spawns:</string>
</property>
</widget>
<widget class="QCheckBox" name="force_offroad_checkBox">
<property name="geometry">
<rect>
<x>910</x>
<y>480</y>
<width>191</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>May help prevent long travel times or pathfinding issues. Tip: You can change this dynamically from mission triggers.</string>
</property>
<property name="text">
<string>Force Offroad</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="tristate">
<bool>false</bool>
</property>
</widget>
<widget class="QCheckBox" name="defense_checkBox">
<property name="geometry">
<rect>
<x>60</x>
<y>90</y>
<width>181</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Defensive Mode</string>
</property>
</widget>
<zorder>background_label</zorder>
<zorder>scenario_comboBox</zorder>
<zorder>scenario_label</zorder>
<zorder>generateButton</zorder>
<zorder>description_textBrowser</zorder>
<zorder>blueforces_comboBox</zorder>
<zorder>scenario_label_2</zorder>
<zorder>scenario_label_3</zorder>
<zorder>redforces_comboBox</zorder>
<zorder>scenario_hint_label</zorder>
<zorder>forces_hint_label</zorder>
<zorder>blueqty_spinBox</zorder>
<zorder>redqty_spinBox</zorder>
<zorder>scenario_label_4</zorder>
<zorder>game_status_checkBox</zorder>
<zorder>voiceovers_checkBox</zorder>
<zorder>logistics_crates_checkBox</zorder>
<zorder>awacs_checkBox</zorder>
<zorder>tankers_checkBox</zorder>
<zorder>apcs_spawn_checkBox</zorder>
<zorder>enemy_transport_checkBox</zorder>
<zorder>enemy_attack_helos_checkBox</zorder>
<zorder>enemy_fighters_checkBox</zorder>
<zorder>enemy_attack_planes_checkBox</zorder>
<zorder>inf_spawn_spinBox</zorder>
<zorder>smoke_checkBox</zorder>
<zorder>scenario_label_5</zorder>
<zorder>forces_hint_label_2</zorder>
<zorder>label</zorder>
<zorder>slot_template_comboBox</zorder>
<zorder>label_2</zorder>
<zorder>scenario_label_6</zorder>
<zorder>force_offroad_checkBox</zorder>
<zorder>defense_checkBox</zorder>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1140</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar">
<property name="acceptDrops">
<bool>false</bool>
</property>
</widget>
<action name="action_generateMission">
<property name="text">
<string>_generateMission</string>
</property>
</action>
<action name="action_scenarioSelected">
<property name="text">
<string>_scenarioSelected</string>
</property>
</action>
<action name="action_blueforcesSelected">
<property name="text">
<string>_blueforcesSelected</string>
</property>
</action>
<action name="action_redforcesSelected">
<property name="text">
<string>_redforcesSelected</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>generateButton</sender>
<signal>clicked()</signal>
<receiver>action_generateMission</receiver>
<slot>trigger()</slot>
<hints>
<hint type="sourcelabel">
<x>993</x>
<y>591</y>
</hint>
<hint type="destinationlabel">
<x>589</x>
<y>409</y>
</hint>
</hints>
</connection>
<connection>
<sender>scenario_comboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>action_scenarioSelected</receiver>
<slot>trigger()</slot>
<hints>
<hint type="sourcelabel">
<x>285</x>
<y>71</y>
</hint>
<hint type="destinationlabel">
<x>-1</x>
<y>-1</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,406 @@
from tokenize import String
import dcs
import os
import random
import RotorOpsUnits
import time
class RotorOpsMission:
conflict_zones = {}
staging_zones = {}
spawn_zones = {}
scripts = {}
def __init__(self):
self.m = dcs.mission.Mission()
self.old_blue = self.m.coalition.get("blue").dict()
self.old_red = self.m.coalition.get("red").dict()
os.chdir("../")
self.home_dir = os.getcwd()
self.scenarios_dir = self.home_dir + "\Generator\Scenarios"
self.forces_dir = self.home_dir + "\Generator\Forces"
self.script_directory = self.home_dir
self.sound_directory = self.home_dir + "\sound\embedded"
self.output_dir = self.home_dir + "\Generator\Output"
self.assets_dir = self.home_dir + "\Generator/assets"
class RotorOpsZone:
def __init__(self, name: str, flag: int, position: dcs.point, size: int):
self.name = name
self.flag = flag
self.position = position
self.size = size
def getMission(self):
return self.m
def addZone(self, zone_dict, zone: RotorOpsZone):
zone_dict[zone.name] = zone
def addResources(self, sound_directory, script_directory):
# add all of our required sounds
os.chdir(sound_directory)
path = os.getcwd()
dir_list = os.listdir(path)
# print("Files and directories in '", path, "' :")
# print(dir_list)
for filename in dir_list:
if filename.endswith(".ogg"):
#print(filename)
self.m.map_resource.add_resource_file(filename)
#add all of our lua scripts
os.chdir(script_directory)
path = os.getcwd()
dir_list = os.listdir(path)
# print("Files and directories in '", path, "' :")
# print(dir_list)
for filename in dir_list:
if filename.endswith(".lua"):
print("Adding script to mission: " + filename)
self.scripts[filename] = self.m.map_resource.add_resource_file(filename)
def getUnitsFromMiz(self, filename, side):
forces = []
os.chdir(self.home_dir)
os.chdir(self.forces_dir + "/" + side)
print("Looking for " + side + " Forces files in '", os.getcwd(), "' :")
source_mission = dcs.mission.Mission()
try:
source_mission.load_file(filename)
for country_name in source_mission.coalition.get(side).countries:
country_obj = source_mission.coalition.get(side).countries[country_name]
for vehicle_group in country_obj.vehicle_group:
forces.append(vehicle_group)
return forces
except:
print("Failed to load units from " + filename)
def generateMission(self, options):
#get the template mission file
os.chdir(self.scenarios_dir)
print("Looking for mission files in '", os.getcwd(), "' :")
self.m.load_file(options["scenario_filename"])
#Load the default coalitions for simplicity
self.m.coalition.get("blue").load_from_dict(self.m, self.old_blue)
self.m.coalition.get("red").load_from_dict(self.m, self.old_red)
red_forces = self.getUnitsFromMiz(options["red_forces_filename"], "red")
blue_forces = self.getUnitsFromMiz(options["blue_forces_filename"], "blue")
# add zones to target mission
for zone in self.m.triggers.zones():
if zone.name == "ALPHA":
self.addZone(self.conflict_zones, self.RotorOpsZone("ALPHA", 101, zone.position, zone.radius))
elif zone.name == "BRAVO":
self.addZone(self.conflict_zones, self.RotorOpsZone("BRAVO", 102, zone.position, zone.radius))
elif zone.name == "CHARLIE":
self.addZone(self.conflict_zones, self.RotorOpsZone("CHARLIE", 103, zone.position, zone.radius))
elif zone.name == "DELTA":
self.addZone(self.conflict_zones, self.RotorOpsZone("DELTA", 104, zone.position, zone.radius))
elif zone.name.rfind("STAGING") >= 0:
self.addZone(self.staging_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
elif zone.name.rfind("SPAWN") >= 0:
self.addZone(self.spawn_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
#add files and triggers necessary for RotorOps.lua script
self.addResources(self.sound_directory, self.script_directory)
self.scriptTriggerSetup(options)
blue_zones = self.staging_zones
red_zones = self.conflict_zones
if options["defending"]:
blue_zones = self.conflict_zones
red_zones = self.staging_zones
#swap airport sides
blue_airports = self.getCoalitionAirports("blue")
red_airports = self.getCoalitionAirports("red")
for airport_name in blue_airports:
self.m.terrain.airports[airport_name].set_red()
for airport_name in red_airports:
self.m.terrain.airports[airport_name].set_blue()
#Add red ground units
for zone_name in red_zones:
if red_forces:
self.addGroundGroups(red_zones[zone_name], self.m.country('Russia'), red_forces, options["red_quantity"])
#Add blue ground units
for zone_name in blue_zones:
if blue_forces:
self.addGroundGroups(blue_zones[zone_name], self.m.country('USA'), blue_forces,
options["blue_quantity"])
#Add player slots
if options["slots"] == "Multiple Slots":
self.addMultiplayerHelos()
else:
for helicopter in dcs.helicopters.helicopter_map:
if helicopter == options["slots"]:
self.addSinglePlayerHelos(dcs.helicopters.helicopter_map[helicopter])
self.addFlights(options)
#Set the Editor Map View
self.m.map.position = self.m.terrain.airports[self.getCoalitionAirports("blue")[0]].position
self.m.map.zoom = 100000
#Save the mission file
print(self.m.triggers.zones())
os.chdir(self.output_dir)
output_filename = options["scenario_filename"].removesuffix('.miz') + " " + time.strftime('%a%H%M%S') + '.miz'
success = self.m.save(output_filename)
return {"success": success, "filename": output_filename, "directory": self.output_dir} #let the UI know the result
def addGroundGroups(self, zone, _country, groups, quantity):
for a in range(0, quantity):
group = random.choice(groups)
unit_types = []
for unit in group.units:
if dcs.vehicles.vehicle_map[unit.type]:
unit_types.append(dcs.vehicles.vehicle_map[unit.type])
country = self.m.country(_country.name)
pos1 = zone.position.point_from_heading(5, 500)
#for i in range(0, quantity):
self.m.vehicle_group_platoon(
country,
zone.name + '-GND ' + str(a+1),
unit_types,
pos1.random_point_within(zone.size / 2, 500),
heading=random.randint(0, 359),
formation=dcs.unitgroup.VehicleGroup.Formation.Scattered,
)
def getCoalitionAirports(self, side: str):
coalition_airports = []
for airport_name in self.m.terrain.airports:
airportobj = self.m.terrain.airports[airport_name]
if airportobj.coalition == str.upper(side):
coalition_airports.append(airport_name)
return coalition_airports
def addSinglePlayerHelos(self, helotype):
friendly_airports = self.getCoalitionAirports("blue")
for airport_name in friendly_airports:
fg = self.m.flight_group_from_airport(self.m.country('USA'), "Player Helos", helotype,
self.m.terrain.airports[airport_name], group_size=2)
fg.units[0].set_player()
def addMultiplayerHelos(self):
friendly_airports = self.getCoalitionAirports("blue")
for airport_name in friendly_airports:
for helotype in RotorOpsUnits.client_helos:
fg = self.m.flight_group_from_airport(self.m.country('USA'), airport_name + " " + helotype.id, helotype,
self.m.terrain.airports[airport_name], group_size=1)
fg.units[0].set_client()
class TrainingScenario():
@staticmethod
def random_orbit(rect: dcs.mapping.Rectangle):
x1 = random.randrange(int(rect.bottom), int(rect.top))
sy = rect.left
y1 = random.randrange(int(sy), int(rect.right))
heading = 90 if y1 < (sy + (rect.right - sy) / 2) else 270
heading = random.randrange(heading - 20, heading + 20)
race_dist = random.randrange(80 * 1000, 120 * 1000)
return dcs.mapping.Point(x1, y1), heading, race_dist
def addFlights(self, options):
usa = self.m.country(dcs.countries.USA.name)
russia = self.m.country(dcs.countries.Russia.name)
friendly_airport = self.m.terrain.airports[self.getCoalitionAirports("blue")[0]]
enemy_airport = self.m.terrain.airports[self.getCoalitionAirports("red")[0]]
orbit_rect = dcs.mapping.Rectangle(
int(friendly_airport.position.x), int(friendly_airport.position.y - 100 * 1000), int(friendly_airport.position.x - 100 * 1000),
int(friendly_airport.position.y))
if options["f_awacs"]:
awacs_name = "AWACS"
awacs_freq = 266
pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
awacs = self.m.awacs_flight(
usa,
awacs_name,
plane_type=dcs.planes.E_3A,
airport=None,
position=pos,
race_distance=race_dist, heading=heading,
altitude=random.randrange(4000, 5500, 100), frequency=awacs_freq)
awacs_escort = self.m.escort_flight(usa, "AWACS Escort", dcs.countries.USA.Plane.F_15C, None, awacs, group_size=2)
awacs_escort.load_loadout("Combat Air Patrol") #not working for f-15
briefing = self.m.description_text() + "\n\n" + awacs_name + " " + str(awacs_freq) + ".00 " + "\n"
self.m.set_description_text(briefing)
if options["f_tankers"]:
t1_name = "Tanker KC_130 Basket"
t1_freq = 253
t1_tac = "61Y"
t2_name = "Tanker KC_135 Boom"
t2_freq = 256
t2_tac = "101Y"
pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
refuel_net = self.m.refuel_flight(
usa,
t1_name,
dcs.planes.KC130,
airport=None,
position=pos,
race_distance=race_dist, heading=heading,
altitude=random.randrange(4000, 5500, 100), speed=750, frequency=t1_freq, tacanchannel=t1_tac)
pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
refuel_rod = self.m.refuel_flight(
usa,
t2_name,
dcs.planes.KC_135,
airport=None,
position=pos,
race_distance=race_dist, heading=heading,
altitude=random.randrange(4000, 5500, 100), frequency=t2_freq, tacanchannel=t2_tac)
briefing = self.m.description_text() + "\n\n" + t1_name + " " + str(t1_freq) + ".00 " + t1_tac + "\n" + t2_name + " " + str(t2_freq) + ".00 " + t2_tac + "\n"
self.m.set_description_text(briefing)
def zone_attack(fg, unit_type):
fg.set_skill(dcs.unit.Skill.Random)
fg.late_activation = True
#fg.load_loadout(unit_type["loadout"])
#task = dcs.task.CAS
#loadout = dcs.planes.Su_25.loadout(task)
#loadout = dcs.planes.Su_25.loadout_by_name("Ground Attack")
#fg.load_task_default_loadout(task)
#fg.load_loadout("Ground Attack")
#fg.load_task_default_loadout(dcs.task.GroundAttack)
#fg.load_loadout("2xB-13L+4xATGM 9M114")
if options["defending"]:
for zone_name in self.conflict_zones:
fg.add_waypoint(self.conflict_zones[zone_name].position, 1000)
else:
for zone_name in reversed(self.conflict_zones):
fg.add_waypoint(self.conflict_zones[zone_name].position, 1000)
fg.add_runway_waypoint(enemy_airport)
fg.land_at(enemy_airport)
if options["e_attack_helos"]:
helo = random.choice(RotorOpsUnits.e_attack_helos)
afg = self.m.flight_group_from_airport(
russia,
"Enemy Attack Helicopters",
helo,
airport=enemy_airport,
maintask=dcs.task.GroundAttack,
start_type=dcs.mission.StartType.Warm,
group_size=2)
zone_attack(afg, helo)
if options["e_attack_planes"]:
plane = random.choice(RotorOpsUnits.e_attack_planes)
afg = self.m.flight_group_from_airport(
russia, "Enemy Attack Planes", plane["type"],
airport=enemy_airport,
maintask=dcs.task.GroundAttack,
start_type=dcs.mission.StartType.Warm,
group_size=2)
zone_attack(afg, plane)
def scriptTriggerSetup(self, options):
#get the boolean value from ui option and convert to lua string
def lb(var):
return str(options[var]).lower()
game_flag = 100
#Add the first trigger
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Scripts")
mytrig.rules.append(dcs.condition.TimeAfter(1))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["mist_4_4_90.lua"]))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["Splash_Damage_2_0.lua"]))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["CTLD.lua"]))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["RotorOps.lua"]))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String((
"--OPTIONS HERE!\n\n" +
"RotorOps.CTLD_crates = " + lb("crates") + "\n\n" +
"RotorOps.CTLD_sound_effects = true\n\n" +
"RotorOps.force_offroad = " + lb("force_offroad") + "\n\n" +
"RotorOps.voice_overs = " + lb("voiceovers") + "\n\n" +
"RotorOps.zone_status_display = " + lb("game_display") + "\n\n" +
"RotorOps.inf_spawns_per_zone = " + lb("inf_spawn_qty") + "\n\n" +
"RotorOps.apcs_spawn_infantry = " + lb("apc_spawns_inf") + " \n\n"))))
self.m.triggerrules.triggers.append(mytrig)
#Add the second trigger
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Zones")
mytrig.rules.append(dcs.condition.TimeAfter(2))
for s_zone in self.staging_zones:
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.stagingZone('" + s_zone + "')")))
for c_zone in self.conflict_zones:
zone_flag = self.conflict_zones[c_zone].flag
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.addZone('" + c_zone + "'," + str(zone_flag) + ")")))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.setupConflict('" + str(game_flag) + "')")))
self.m.triggerrules.triggers.append(mytrig)
#Add the third trigger
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict Start")
mytrig.rules.append(dcs.condition.TimeAfter(10))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.startConflict(100)")))
self.m.triggerrules.triggers.append(mytrig)
#Add all zone-based triggers
for index, c_zone in enumerate(self.conflict_zones):
z_active_trig = dcs.triggers.TriggerOnce(comment= c_zone + " Active")
z_active_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1))
z_active_trig.actions.append(dcs.action.DoScript(dcs.action.String("--Add any action you want here!")))
self.m.triggerrules.triggers.append(z_active_trig)
zone_flag = self.conflict_zones[c_zone].flag
z_weak_trig = dcs.triggers.TriggerOnce(comment= c_zone + " Weak")
z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone_flag, 10))
z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone_flag, random.randrange(20, 80)))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("--Add any action you want here!\n\n--Flag value represents the percentage of defending ground units remaining. ")))
if options["e_attack_helos"]:
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("mist.respawnGroup('Enemy Attack Helicopters', true)")))
self.m.triggerrules.triggers.append(z_weak_trig)
#Add game won/lost triggers
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict WON")
mytrig.rules.append(dcs.condition.FlagEquals(game_flag, 99))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is WON")))
self.m.triggerrules.triggers.append(mytrig)
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict LOST")
mytrig.rules.append(dcs.condition.FlagEquals(game_flag, 98))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is LOST")))
self.m.triggerrules.triggers.append(mytrig)

View File

@@ -0,0 +1,26 @@
import dcs
client_helos = [
dcs.helicopters.UH_1H,
dcs.helicopters.Mi_8MT,
dcs.helicopters.Mi_24P,
dcs.helicopters.Ka_50,
]
e_attack_helos = [
dcs.helicopters.Mi_24P,
dcs.helicopters.Ka_50,
dcs.helicopters.Mi_28N,
]
e_transport_helos = [
dcs.helicopters.Mi_26,
dcs.helicopters.Mi_24P,
dcs.helicopters.Mi_8MT,
]
e_attack_planes = [
{'type': dcs.planes.Su_34, 'loadout': "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410"},
{'type': dcs.planes.Su_25, 'loadout': "RKB-250*8,R-60M*2"},
]

View File

@@ -0,0 +1,17 @@
import math
import dcs
def getDistance(point1=dcs.Point, point2=dcs.Point):
x1 = point1.x
y1 = point1.y
x2 = point2.x
y2 = point2.y
dX = abs(x1-x2)
dY = abs(y1-y2)
distance = math.sqrt(dX*dX + dY*dY)
return distance
def convertMeterToNM(meters=int):
nm = meters / 1852
return nm

View File

@@ -0,0 +1,20 @@
You can add your own scenarios in this directory and they will appear in the mission generator.
A scenario .miz file MUST have:
1) Between 1-4 trigger zones called "ALPHA", "BRAVO", "CHARLIE", "DELTA"
2) At least one trigger zone with a name that starts with "STAGING".
3) A blue airport (recommend somewhere near your staging zone).
4) A red airport (recommend somewhere near your last conflict zone).
Optional:
You can add smaller infantry spawning zones inside conflict zones. Add near buildings to simulate infantry hiding within. Name them like "ALPHA_SPAWN", "ALPHA_SPAWN_2, etc.
Tips:
-The conflict game type can be played with blue forces on defense. In this mode the last conflict zone is the only troop pickup zone.
-Design your template so that it can be played in normal 'attacking' mode or 'defending' the conflict zone from enemy ground units starting from the staging zone.
-Keep the zones fairly close together, both for helicopter and ground unit travel times.
-You can place static objects and units in a scenario template.
-You can change mission briefing and other mission options in the template.
-Drop your templates in the RotorOps Discord if you'd like to have them added in a release for everyone. Maintain a similar naming convention and be sure to credit yourself in the .miz name.
-Airfields can be captured with ground units. You might consider placing conflict zones over neutral airfields and adding unarmed client slots.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
Generator/assets/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

BIN
Generator/dist/MissionGenerator.exe vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,5 @@
1) Place your .miz file in this directory.
2) Run embed_sounds.exe
3) A new file will be created in the same directory; a copy of your mission file with RotorOps sound files already embedded so you don't need to add manually.
Tip: You can use this tool to update your existing RotorOps missions with the current sound files.

View File

@@ -0,0 +1,37 @@
import dcs
import os
from tkinter import messagebox as mbox
mizfound = False
path = os.getcwd()
dir_list = os.listdir(path)
print("Looking for mission files in '", path, "' :")
for filename in dir_list:
if filename.endswith(".miz") and not filename == "template_source.miz" and not filename.startswith("SoundsAdded"):
mizfound = True
print("Attempting to add sound files to: " + filename)
m = dcs.mission.Mission()
m.load_file(filename)
# add all of our required sounds
os.chdir("../sound/embedded")
path = os.getcwd()
sound_file_list = os.listdir(path)
print("Attempting to add sound files from '", path, "' :")
for soundfilename in sound_file_list:
if soundfilename.endswith(".ogg"):
print("Adding " + soundfilename)
m.map_resource.add_resource_file(soundfilename)
continue
else:
continue
os.chdir("../../Generator")
m.save("SoundsAdded_" + filename)
if not mizfound:
print("No valid miz files found!")
mbox.showerror('No Source Files Found', 'Error: Place your .miz files in this directory before running the application.')

View File

@@ -0,0 +1,40 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['embed_sounds.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='embed_sounds',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None )