Merge pull request #29 from spencershepard/feature/updater
Feature/updater
4
.gitignore
vendored
@ -13,3 +13,7 @@ incoming templates/
|
||||
Generator/utils/extract units/source.miz
|
||||
Generator/utils/extract units/units.txt
|
||||
generator.log
|
||||
templates/Scenarios/user
|
||||
templates/Scenarios/downloaded
|
||||
config/user-data.yaml
|
||||
*.exe
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
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.
|
||||
|
||||
Optional:
|
||||
4) Add helicopters with "CAS" main task for attack helicopters.
|
||||
5) Add helicopters with "Transport" main task for transport helicopters.
|
||||
6) Add planes with "CAS" main task for attack planes.
|
||||
7) Add planes with "CAP" main task for fighters.
|
||||
8) Configure loadouts, liveries, and skill for aircraft.
|
||||
|
||||
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.
|
||||
-Only unit types are used from ground units. Liveries or other attributes are able to be copied.
|
||||
-For aircraft, group size is currently capped at 2 units per group to help prevent issues with parking. Only the first unit in the group is used as a source.
|
||||
@ -1,17 +0,0 @@
|
||||
You can put .miz files in this folder to be copied into the generated mission at marker points. This feature is currently very 'alpha' and may produce errors. Currently, this doesn't work for ship groups or plane groups.
|
||||
|
||||
1) Make an empty mission on Cauacasus.
|
||||
|
||||
2) Place units/objects on the map.
|
||||
|
||||
3) Make one unit group name: 'ANCHOR' This will represent the point of insertion into the target mission.
|
||||
|
||||
4) In a Scenario template, place a static object (flag, etc) and call it "IMPORT-[filename of .miz created in first step]" Country should be CJTF Red, CJTF Blue, or UN Peacekeepers.
|
||||
|
||||
5) Change the unit name of the object created in the previous step. This unit name might be used for spawn names, so you should call the unit name something like "North Base" so players know where they'll be spawning when choosing a slot.
|
||||
|
||||
|
||||
Tips:
|
||||
-You can change the heading of the imported group by changing the heading of the insertion object.
|
||||
-For multiple imports of the same template, the import object group name should end with '-01' or '-whatever'.
|
||||
|
||||
@ -27,7 +27,7 @@ exe = EXE(pyz,
|
||||
a.datas,
|
||||
[],
|
||||
name='MissionGenerator',
|
||||
icon='assets\\icon.ico',
|
||||
icon='..\\assets\\icon.ico',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
||||
118
Generator/MissionGeneratorScenario.py
Normal file
@ -0,0 +1,118 @@
|
||||
from MissionGenerator import directories
|
||||
import os
|
||||
import RotorOpsUtils
|
||||
import dcs
|
||||
import math
|
||||
from os.path import exists
|
||||
|
||||
class Scenario:
|
||||
def __init__(self, path, name):
|
||||
self.path = path
|
||||
self.name = name
|
||||
self.description = ""
|
||||
# self.image_path = None
|
||||
self.map_name = None
|
||||
self.config = None
|
||||
self.downloadable = False
|
||||
self.tags = []
|
||||
self.rating = None
|
||||
self.rating_qty = None
|
||||
self.packageID = None
|
||||
self.local_rating = None
|
||||
|
||||
def applyConfig(self, config):
|
||||
self.config = config
|
||||
if 'description' in config:
|
||||
self.description = config["description"]
|
||||
if 'name' in config:
|
||||
self.name = config["name"]
|
||||
if 'map' in config:
|
||||
self.map_name = config["map"]
|
||||
if 'tags' in config:
|
||||
for tag in config['tags']:
|
||||
self.tags.append(tag)
|
||||
|
||||
|
||||
|
||||
def evaluateMiz(self):
|
||||
# check if we have the miz file
|
||||
if exists(self.path):
|
||||
self.exists = True
|
||||
else:
|
||||
self.exists = False
|
||||
return None
|
||||
|
||||
source_mission = dcs.mission.Mission()
|
||||
source_mission.load_file(self.path)
|
||||
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
|
||||
|
||||
|
||||
|
||||
for zone in zones:
|
||||
if zone.name.rfind("STAGING") == 0:
|
||||
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
|
||||
|
||||
description = ""
|
||||
|
||||
if self.rating:
|
||||
description = description + "Rated " + str(self.rating) + "/5 based on " + str(self.rating_qty) + " reviews!\n"
|
||||
|
||||
if self.config:
|
||||
|
||||
if 'name' in self.config and self.config["name"] is not None:
|
||||
description = description + '<h4>' + self.config["name"] + '</h4>'
|
||||
if 'description' in self.config and self.config["description"] is not None:
|
||||
description = description + self.config["description"] + "\n\n"
|
||||
|
||||
if conflict_zones and staging_zones :
|
||||
average_zone_size = conflict_zone_size_sum / conflict_zones
|
||||
description = (
|
||||
description +
|
||||
"Map: " + source_mission.terrain.name + "\n" +
|
||||
"Conflict Zones: " + str(conflict_zones) + "\n" +
|
||||
"Staging Zones: " + str(staging_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())
|
||||
+ "\n== BRIEFING ==\n\n"
|
||||
+ source_mission.description_text()
|
||||
)
|
||||
if self.packageID:
|
||||
description = description + "\n\nScenario module ID: " + self.packageID
|
||||
self.description = description.replace("\n", "<br />")
|
||||
|
||||
|
||||
|
||||
@ -30,70 +30,12 @@ class Ui_MainWindow(object):
|
||||
MainWindow.setWindowIcon(icon)
|
||||
MainWindow.setWindowOpacity(4.0)
|
||||
MainWindow.setAutoFillBackground(False)
|
||||
MainWindow.setStyleSheet("/*-----QScrollBar-----*/\n"
|
||||
"QScrollBar:horizontal \n"
|
||||
"{\n"
|
||||
" background-color: transparent;\n"
|
||||
" height: 8px;\n"
|
||||
" margin: 0px;\n"
|
||||
" padding: 0px;\n"
|
||||
"\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"QScrollBar::handle:horizontal \n"
|
||||
"{\n"
|
||||
" border: none;\n"
|
||||
" min-width: 100px;\n"
|
||||
" background-color: #9b9b9b;\n"
|
||||
"\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"QScrollBar::add-line:horizontal, \n"
|
||||
"QScrollBar::sub-line:horizontal,\n"
|
||||
"QScrollBar::add-page:horizontal, \n"
|
||||
"QScrollBar::sub-page:horizontal \n"
|
||||
"{\n"
|
||||
" width: 0px;\n"
|
||||
" background-color: transparent;\n"
|
||||
"\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"QScrollBar:vertical \n"
|
||||
"{\n"
|
||||
" background-color: transparent;\n"
|
||||
" width: 8px;\n"
|
||||
" margin: 0;\n"
|
||||
"\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"QScrollBar::handle:vertical \n"
|
||||
"{\n"
|
||||
" border: none;\n"
|
||||
" min-height: 100px;\n"
|
||||
" background-color: #9b9b9b;\n"
|
||||
"\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"QScrollBar::add-line:vertical, \n"
|
||||
"QScrollBar::sub-line:vertical,\n"
|
||||
"QScrollBar::add-page:vertical, \n"
|
||||
"QScrollBar::sub-page:vertical \n"
|
||||
"{\n"
|
||||
" height: 0px;\n"
|
||||
" background-color: transparent;\n"
|
||||
"\n"
|
||||
"}")
|
||||
MainWindow.setStyleSheet("")
|
||||
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.logistics_crates_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.logistics_crates_checkBox.setGeometry(QtCore.QRect(990, 211, 251, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.logistics_crates_checkBox.setFont(font)
|
||||
@ -102,7 +44,6 @@ class Ui_MainWindow(object):
|
||||
self.zone_sams_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.zone_sams_checkBox.setGeometry(QtCore.QRect(990, 320, 241, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.zone_sams_checkBox.setFont(font)
|
||||
@ -110,15 +51,13 @@ class Ui_MainWindow(object):
|
||||
self.red_forces_label = QtWidgets.QLabel(self.centralwidget)
|
||||
self.red_forces_label.setGeometry(QtCore.QRect(470, 80, 171, 27))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.red_forces_label.setFont(font)
|
||||
self.red_forces_label.setObjectName("red_forces_label")
|
||||
self.scenario_comboBox = QtWidgets.QComboBox(self.centralwidget)
|
||||
self.scenario_comboBox.setGeometry(QtCore.QRect(30, 20, 371, 29))
|
||||
self.scenario_comboBox.setGeometry(QtCore.QRect(40, 10, 351, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(8)
|
||||
font.setBold(True)
|
||||
self.scenario_comboBox.setFont(font)
|
||||
@ -129,9 +68,8 @@ class Ui_MainWindow(object):
|
||||
self.scenario_comboBox.setFrame(True)
|
||||
self.scenario_comboBox.setObjectName("scenario_comboBox")
|
||||
self.description_textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
|
||||
self.description_textBrowser.setGeometry(QtCore.QRect(40, 410, 361, 251))
|
||||
self.description_textBrowser.setGeometry(QtCore.QRect(30, 390, 371, 211))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.description_textBrowser.setFont(font)
|
||||
self.description_textBrowser.setStyleSheet("padding: 5px;")
|
||||
@ -143,7 +81,6 @@ class Ui_MainWindow(object):
|
||||
self.defense_checkBox.setEnabled(True)
|
||||
self.defense_checkBox.setGeometry(QtCore.QRect(470, 120, 156, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.defense_checkBox.setFont(font)
|
||||
@ -167,7 +104,6 @@ class Ui_MainWindow(object):
|
||||
sizePolicy.setHeightForWidth(self.redforces_comboBox.sizePolicy().hasHeightForWidth())
|
||||
self.redforces_comboBox.setSizePolicy(sizePolicy)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
font.setBold(False)
|
||||
self.redforces_comboBox.setFont(font)
|
||||
@ -175,7 +111,6 @@ class Ui_MainWindow(object):
|
||||
self.scenario_label_8 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.scenario_label_8.setGeometry(QtCore.QRect(570, 220, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.scenario_label_8.setFont(font)
|
||||
@ -183,7 +118,6 @@ class Ui_MainWindow(object):
|
||||
self.slot_template_comboBox = QtWidgets.QComboBox(self.centralwidget)
|
||||
self.slot_template_comboBox.setGeometry(QtCore.QRect(960, 384, 271, 33))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.slot_template_comboBox.setFont(font)
|
||||
@ -198,7 +132,6 @@ class Ui_MainWindow(object):
|
||||
self.blue_forces_label = QtWidgets.QLabel(self.centralwidget)
|
||||
self.blue_forces_label.setGeometry(QtCore.QRect(470, 30, 161, 27))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.blue_forces_label.setFont(font)
|
||||
@ -216,7 +149,6 @@ class Ui_MainWindow(object):
|
||||
self.blueforces_comboBox = QtWidgets.QComboBox(self.centralwidget)
|
||||
self.blueforces_comboBox.setGeometry(QtCore.QRect(660, 30, 391, 33))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
font.setBold(False)
|
||||
self.blueforces_comboBox.setFont(font)
|
||||
@ -235,7 +167,6 @@ class Ui_MainWindow(object):
|
||||
self.scenario_label_10 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.scenario_label_10.setGeometry(QtCore.QRect(570, 260, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.scenario_label_10.setFont(font)
|
||||
@ -290,7 +221,6 @@ class Ui_MainWindow(object):
|
||||
self.scenario_label_7 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.scenario_label_7.setGeometry(QtCore.QRect(570, 180, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.scenario_label_7.setFont(font)
|
||||
@ -298,7 +228,6 @@ class Ui_MainWindow(object):
|
||||
self.label_2 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.label_2.setGeometry(QtCore.QRect(840, 390, 111, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.label_2.setFont(font)
|
||||
@ -306,57 +235,42 @@ class Ui_MainWindow(object):
|
||||
self.scenario_label_9 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.scenario_label_9.setGeometry(QtCore.QRect(490, 450, 251, 23))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
self.scenario_label_9.setFont(font)
|
||||
self.scenario_label_9.setObjectName("scenario_label_9")
|
||||
self.awacs_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.awacs_checkBox.setGeometry(QtCore.QRect(990, 246, 241, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.awacs_checkBox.setFont(font)
|
||||
self.awacs_checkBox.setStatusTip("")
|
||||
self.awacs_checkBox.setChecked(True)
|
||||
self.awacs_checkBox.setObjectName("awacs_checkBox")
|
||||
self.tankers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.tankers_checkBox.setGeometry(QtCore.QRect(990, 282, 241, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.tankers_checkBox.setFont(font)
|
||||
self.tankers_checkBox.setChecked(True)
|
||||
self.tankers_checkBox.setObjectName("tankers_checkBox")
|
||||
self.inf_spawn_voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.inf_spawn_voiceovers_checkBox.setGeometry(QtCore.QRect(960, 455, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.inf_spawn_voiceovers_checkBox.setFont(font)
|
||||
self.inf_spawn_voiceovers_checkBox.setChecked(True)
|
||||
self.inf_spawn_voiceovers_checkBox.setObjectName("inf_spawn_voiceovers_checkBox")
|
||||
self.voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.voiceovers_checkBox.setGeometry(QtCore.QRect(960, 517, 171, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.voiceovers_checkBox.setFont(font)
|
||||
self.voiceovers_checkBox.setChecked(True)
|
||||
self.voiceovers_checkBox.setObjectName("voiceovers_checkBox")
|
||||
self.smoke_pickup_zone_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.smoke_pickup_zone_checkBox.setGeometry(QtCore.QRect(960, 424, 271, 24))
|
||||
self.smoke_pickup_zone_checkBox.setGeometry(QtCore.QRect(960, 460, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.smoke_pickup_zone_checkBox.setFont(font)
|
||||
self.smoke_pickup_zone_checkBox.setChecked(False)
|
||||
self.smoke_pickup_zone_checkBox.setObjectName("smoke_pickup_zone_checkBox")
|
||||
self.game_status_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.game_status_checkBox.setGeometry(QtCore.QRect(960, 486, 271, 24))
|
||||
self.game_status_checkBox.setGeometry(QtCore.QRect(960, 490, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.game_status_checkBox.setFont(font)
|
||||
self.game_status_checkBox.setChecked(True)
|
||||
@ -365,7 +279,6 @@ class Ui_MainWindow(object):
|
||||
self.label = QtWidgets.QLabel(self.centralwidget)
|
||||
self.label.setGeometry(QtCore.QRect(570, 380, 261, 23))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.label.setFont(font)
|
||||
@ -393,7 +306,6 @@ class Ui_MainWindow(object):
|
||||
self.force_offroad_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.force_offroad_checkBox.setGeometry(QtCore.QRect(960, 548, 161, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.force_offroad_checkBox.setFont(font)
|
||||
self.force_offroad_checkBox.setChecked(False)
|
||||
@ -402,7 +314,6 @@ class Ui_MainWindow(object):
|
||||
self.label_3 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.label_3.setGeometry(QtCore.QRect(570, 330, 281, 23))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.label_3.setFont(font)
|
||||
@ -410,31 +321,23 @@ class Ui_MainWindow(object):
|
||||
self.apcs_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(990, 180, 251, 27))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.apcs_spawn_checkBox.setFont(font)
|
||||
self.apcs_spawn_checkBox.setChecked(True)
|
||||
self.apcs_spawn_checkBox.setObjectName("apcs_spawn_checkBox")
|
||||
self.generateButton = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.generateButton.setEnabled(True)
|
||||
self.generateButton.setGeometry(QtCore.QRect(710, 600, 231, 51))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(8)
|
||||
font.setBold(True)
|
||||
self.generateButton.setFont(font)
|
||||
self.generateButton.setStyleSheet("background-color: gray;\n"
|
||||
"color: rgb(255, 255, 255);\n"
|
||||
"border-style: outset;\n"
|
||||
"border-width: 1px;\n"
|
||||
"border-radius: 5px;\n"
|
||||
"border-color: black;\n"
|
||||
"padding: 4px;")
|
||||
self.generateButton.setStyleSheet("")
|
||||
self.generateButton.setObjectName("generateButton")
|
||||
self.farp_always = QtWidgets.QRadioButton(self.centralwidget)
|
||||
self.farp_always.setGeometry(QtCore.QRect(510, 480, 261, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.farp_always.setFont(font)
|
||||
self.farp_always.setObjectName("farp_always")
|
||||
@ -444,7 +347,6 @@ class Ui_MainWindow(object):
|
||||
self.farp_never = QtWidgets.QRadioButton(self.centralwidget)
|
||||
self.farp_never.setGeometry(QtCore.QRect(510, 540, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.farp_never.setFont(font)
|
||||
self.farp_never.setObjectName("farp_never")
|
||||
@ -452,7 +354,6 @@ class Ui_MainWindow(object):
|
||||
self.farp_gunits = QtWidgets.QRadioButton(self.centralwidget)
|
||||
self.farp_gunits.setGeometry(QtCore.QRect(510, 509, 261, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
self.farp_gunits.setFont(font)
|
||||
self.farp_gunits.setChecked(True)
|
||||
@ -460,7 +361,7 @@ class Ui_MainWindow(object):
|
||||
self.farp_buttonGroup.addButton(self.farp_gunits)
|
||||
self.missionImage = QtWidgets.QLabel(self.centralwidget)
|
||||
self.missionImage.setEnabled(True)
|
||||
self.missionImage.setGeometry(QtCore.QRect(60, 80, 300, 300))
|
||||
self.missionImage.setGeometry(QtCore.QRect(60, 60, 310, 310))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
@ -470,39 +371,93 @@ class Ui_MainWindow(object):
|
||||
self.missionImage.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
||||
self.missionImage.setStyleSheet("")
|
||||
self.missionImage.setText("")
|
||||
self.missionImage.setPixmap(QtGui.QPixmap("assets/briefing1.png"))
|
||||
self.missionImage.setPixmap(QtGui.QPixmap("../assets/briefing1.png"))
|
||||
self.missionImage.setScaledContents(True)
|
||||
self.missionImage.setWordWrap(False)
|
||||
self.missionImage.setObjectName("missionImage")
|
||||
self.nextScenario_pushButton = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.nextScenario_pushButton.setGeometry(QtCore.QRect(370, 210, 31, 51))
|
||||
self.nextScenario_pushButton.setGeometry(QtCore.QRect(350, 620, 41, 31))
|
||||
self.nextScenario_pushButton.setObjectName("nextScenario_pushButton")
|
||||
self.prevScenario_pushButton = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.prevScenario_pushButton.setGeometry(QtCore.QRect(20, 210, 31, 51))
|
||||
self.prevScenario_pushButton.setGeometry(QtCore.QRect(40, 620, 41, 31))
|
||||
self.prevScenario_pushButton.setObjectName("prevScenario_pushButton")
|
||||
self.background_label = QtWidgets.QLabel(self.centralwidget)
|
||||
self.background_label.setGeometry(QtCore.QRect(1020, 600, 241, 51))
|
||||
self.background_label.setText("")
|
||||
self.background_label.setPixmap(QtGui.QPixmap("assets/rotorops-dkgray.png"))
|
||||
self.background_label.setPixmap(QtGui.QPixmap("../assets/rotorops-dkgray.png"))
|
||||
self.background_label.setScaledContents(True)
|
||||
self.background_label.setObjectName("background_label")
|
||||
self.rateButton1 = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.rateButton1.setEnabled(True)
|
||||
self.rateButton1.setGeometry(QtCore.QRect(120, 620, 31, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(8)
|
||||
self.rateButton1.setFont(font)
|
||||
self.rateButton1.setStyleSheet("border-image:url(\'../assets/star_full.png\');")
|
||||
self.rateButton1.setText("")
|
||||
self.rateButton1.setObjectName("rateButton1")
|
||||
self.hotstart_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.hotstart_checkBox.setGeometry(QtCore.QRect(960, 430, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.hotstart_checkBox.setFont(font)
|
||||
self.hotstart_checkBox.setChecked(False)
|
||||
self.hotstart_checkBox.setTristate(False)
|
||||
self.hotstart_checkBox.setObjectName("hotstart_checkBox")
|
||||
self.rateButton2 = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.rateButton2.setEnabled(True)
|
||||
self.rateButton2.setGeometry(QtCore.QRect(160, 620, 31, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(8)
|
||||
self.rateButton2.setFont(font)
|
||||
self.rateButton2.setStyleSheet("border-image:url(\'../assets/star_full.png\');")
|
||||
self.rateButton2.setText("")
|
||||
self.rateButton2.setObjectName("rateButton2")
|
||||
self.rateButton3 = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.rateButton3.setEnabled(True)
|
||||
self.rateButton3.setGeometry(QtCore.QRect(200, 620, 31, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(8)
|
||||
self.rateButton3.setFont(font)
|
||||
self.rateButton3.setStyleSheet("border-image:url(\'../assets/star_full.png\');")
|
||||
self.rateButton3.setText("")
|
||||
self.rateButton3.setObjectName("rateButton3")
|
||||
self.rateButton4 = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.rateButton4.setEnabled(True)
|
||||
self.rateButton4.setGeometry(QtCore.QRect(240, 620, 31, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(8)
|
||||
self.rateButton4.setFont(font)
|
||||
self.rateButton4.setStyleSheet("border-image:url(\'../assets/star_full.png\');")
|
||||
self.rateButton4.setText("")
|
||||
self.rateButton4.setObjectName("rateButton4")
|
||||
self.rateButton5 = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.rateButton5.setEnabled(True)
|
||||
self.rateButton5.setGeometry(QtCore.QRect(280, 620, 31, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(8)
|
||||
self.rateButton5.setFont(font)
|
||||
self.rateButton5.setStyleSheet("border-image:url(\'../assets/star_full.png\');")
|
||||
self.rateButton5.setText("")
|
||||
self.rateButton5.setObjectName("rateButton5")
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 26))
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 29))
|
||||
self.menubar.setObjectName("menubar")
|
||||
self.menuMap = QtWidgets.QMenu(self.menubar)
|
||||
self.menuMap.setObjectName("menuMap")
|
||||
self.menuGametype_Filter = QtWidgets.QMenu(self.menubar)
|
||||
self.menuGametype_Filter.setObjectName("menuGametype_Filter")
|
||||
self.menuFilter = QtWidgets.QMenu(self.menubar)
|
||||
self.menuFilter.setObjectName("menuFilter")
|
||||
self.menuPreferences = QtWidgets.QMenu(self.menubar)
|
||||
self.menuPreferences.setObjectName("menuPreferences")
|
||||
MainWindow.setMenuBar(self.menubar)
|
||||
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Arial")
|
||||
font.setPointSize(9)
|
||||
font.setBold(False)
|
||||
self.statusbar.setFont(font)
|
||||
self.statusbar.setAcceptDrops(False)
|
||||
self.statusbar.setStyleSheet("color: rgb(255, 255, 255);")
|
||||
self.statusbar.setStyleSheet("")
|
||||
self.statusbar.setObjectName("statusbar")
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
self.action_generateMission = QtWidgets.QAction(MainWindow)
|
||||
@ -520,43 +475,90 @@ class Ui_MainWindow(object):
|
||||
self.action_prevScenario = QtWidgets.QAction(MainWindow)
|
||||
self.action_prevScenario.setObjectName("action_prevScenario")
|
||||
self.actionCaucasus = QtWidgets.QAction(MainWindow)
|
||||
self.actionCaucasus.setCheckable(True)
|
||||
self.actionCaucasus.setChecked(True)
|
||||
self.actionCaucasus.setObjectName("actionCaucasus")
|
||||
self.actionPersian_Gulf = QtWidgets.QAction(MainWindow)
|
||||
self.actionPersian_Gulf.setCheckable(True)
|
||||
self.actionPersian_Gulf.setChecked(True)
|
||||
self.actionPersian_Gulf.setObjectName("actionPersian_Gulf")
|
||||
self.actionMarianas = QtWidgets.QAction(MainWindow)
|
||||
self.actionMarianas.setCheckable(True)
|
||||
self.actionMarianas.setChecked(True)
|
||||
self.actionMarianas.setObjectName("actionMarianas")
|
||||
self.actionNevada = QtWidgets.QAction(MainWindow)
|
||||
self.actionNevada.setCheckable(True)
|
||||
self.actionNevada.setChecked(True)
|
||||
self.actionNevada.setObjectName("actionNevada")
|
||||
self.actionSyria = QtWidgets.QAction(MainWindow)
|
||||
self.actionSyria.setCheckable(True)
|
||||
self.actionSyria.setChecked(True)
|
||||
self.actionSyria.setObjectName("actionSyria")
|
||||
self.actionAll = QtWidgets.QAction(MainWindow)
|
||||
self.actionAll.setCheckable(True)
|
||||
self.actionAll.setChecked(True)
|
||||
self.actionAll.setObjectName("actionAll")
|
||||
self.actionMultiplayer = QtWidgets.QAction(MainWindow)
|
||||
self.actionMultiplayer.setCheckable(False)
|
||||
self.actionMultiplayer.setCheckable(True)
|
||||
self.actionMultiplayer.setChecked(True)
|
||||
self.actionMultiplayer.setObjectName("actionMultiplayer")
|
||||
self.actionAll_2 = QtWidgets.QAction(MainWindow)
|
||||
self.actionAll_2.setCheckable(True)
|
||||
self.actionAll_2.setChecked(True)
|
||||
self.actionAll_2.setObjectName("actionAll_2")
|
||||
self.menuMap.addAction(self.actionAll_2)
|
||||
self.actionSave_Directory = QtWidgets.QAction(MainWindow)
|
||||
self.actionSave_Directory.setObjectName("actionSave_Directory")
|
||||
self.action_slotChanged = QtWidgets.QAction(MainWindow)
|
||||
self.action_slotChanged.setObjectName("action_slotChanged")
|
||||
self.actionIncluded = QtWidgets.QAction(MainWindow)
|
||||
self.actionIncluded.setCheckable(True)
|
||||
self.actionIncluded.setChecked(True)
|
||||
self.actionIncluded.setObjectName("actionIncluded")
|
||||
self.actionUser = QtWidgets.QAction(MainWindow)
|
||||
self.actionUser.setObjectName("actionUser")
|
||||
self.actionDownloaded = QtWidgets.QAction(MainWindow)
|
||||
self.actionDownloaded.setObjectName("actionDownloaded")
|
||||
self.action_downloadButton = QtWidgets.QAction(MainWindow)
|
||||
self.action_downloadButton.setObjectName("action_downloadButton")
|
||||
self.action_rateButton1 = QtWidgets.QAction(MainWindow)
|
||||
self.action_rateButton1.setObjectName("action_rateButton1")
|
||||
self.actionSingle_Player = QtWidgets.QAction(MainWindow)
|
||||
self.actionSingle_Player.setCheckable(True)
|
||||
self.actionSingle_Player.setChecked(True)
|
||||
self.actionSingle_Player.setObjectName("actionSingle_Player")
|
||||
self.actionCo_Op = QtWidgets.QAction(MainWindow)
|
||||
self.actionCo_Op.setCheckable(True)
|
||||
self.actionCo_Op.setChecked(True)
|
||||
self.actionCo_Op.setObjectName("actionCo_Op")
|
||||
self.actionMapMenu = QtWidgets.QAction(MainWindow)
|
||||
self.actionMapMenu.setObjectName("actionMapMenu")
|
||||
self.actionFilterMenu = QtWidgets.QAction(MainWindow)
|
||||
self.actionFilterMenu.setObjectName("actionFilterMenu")
|
||||
self.action_rateButton2 = QtWidgets.QAction(MainWindow)
|
||||
self.action_rateButton2.setObjectName("action_rateButton2")
|
||||
self.action_rateButton3 = QtWidgets.QAction(MainWindow)
|
||||
self.action_rateButton3.setObjectName("action_rateButton3")
|
||||
self.action_rateButton4 = QtWidgets.QAction(MainWindow)
|
||||
self.action_rateButton4.setObjectName("action_rateButton4")
|
||||
self.action_rateButton5 = QtWidgets.QAction(MainWindow)
|
||||
self.action_rateButton5.setObjectName("action_rateButton5")
|
||||
self.menuMap.addAction(self.actionCaucasus)
|
||||
self.menuMap.addAction(self.actionPersian_Gulf)
|
||||
self.menuMap.addAction(self.actionMarianas)
|
||||
self.menuMap.addAction(self.actionNevada)
|
||||
self.menuMap.addAction(self.actionSyria)
|
||||
self.menuGametype_Filter.addAction(self.actionAll)
|
||||
self.menuGametype_Filter.addAction(self.actionMultiplayer)
|
||||
self.menuFilter.addAction(self.actionMultiplayer)
|
||||
self.menuFilter.addAction(self.actionSingle_Player)
|
||||
self.menuFilter.addAction(self.actionCo_Op)
|
||||
self.menuPreferences.addAction(self.actionSave_Directory)
|
||||
self.menubar.addAction(self.menuMap.menuAction())
|
||||
self.menubar.addAction(self.menuGametype_Filter.menuAction())
|
||||
self.menubar.addAction(self.menuFilter.menuAction())
|
||||
self.menubar.addAction(self.menuPreferences.menuAction())
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.generateButton.clicked.connect(self.action_generateMission.trigger)
|
||||
self.scenario_comboBox.currentIndexChanged['int'].connect(self.action_scenarioSelected.trigger)
|
||||
self.defense_checkBox.stateChanged['int'].connect(self.action_defensiveModeChanged.trigger)
|
||||
self.nextScenario_pushButton.clicked.connect(self.action_nextScenario.trigger)
|
||||
self.prevScenario_pushButton.clicked.connect(self.action_prevScenario.trigger)
|
||||
self.defense_checkBox.clicked.connect(self.action_defensiveModeChanged.trigger)
|
||||
self.slot_template_comboBox.activated['int'].connect(self.action_slotChanged.trigger)
|
||||
self.scenario_comboBox.currentIndexChanged['int'].connect(self.action_scenarioSelected.trigger)
|
||||
self.nextScenario_pushButton.clicked.connect(self.action_nextScenario.trigger)
|
||||
self.rateButton1.clicked.connect(self.action_rateButton1.trigger)
|
||||
self.rateButton2.clicked.connect(self.action_rateButton2.trigger)
|
||||
self.rateButton3.clicked.connect(self.action_rateButton3.trigger)
|
||||
self.rateButton4.clicked.connect(self.action_rateButton4.trigger)
|
||||
self.rateButton5.clicked.connect(self.action_rateButton5.trigger)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
@ -571,17 +573,17 @@ class Ui_MainWindow(object):
|
||||
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\" /><meta charset=\"utf-8\" /><style type=\"text/css\">\n"
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:\'Arial\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||
"</style></head><body style=\" font-family:\'Segoe UI\'; 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-family:\'MS Shell Dlg 2\'; font-size:10pt;\">Provide close air support for our convoys as we take back Las Vegas from the enemy!</span></p></body></html>"))
|
||||
self.defense_checkBox.setText(_translate("MainWindow", "Blue on Defense"))
|
||||
self.redqty_spinBox.setStatusTip(_translate("MainWindow", "How many groups should we generate?"))
|
||||
self.redqty_spinBox.setStatusTip(_translate("MainWindow", "Red vehicle groups per staging or conflict zone."))
|
||||
self.redforces_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own custom ground forces groups to be automatically generated."))
|
||||
self.scenario_label_8.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack plane group spawns."))
|
||||
self.scenario_label_8.setText(_translate("MainWindow", "Enemy Attack Planes"))
|
||||
self.slot_template_comboBox.setStatusTip(_translate("MainWindow", "Default player/client spawn locations at a friendly airport."))
|
||||
self.scenario_label_5.setText(_translate("MainWindow", "Groups Per Zone"))
|
||||
self.blue_forces_label.setText(_translate("MainWindow", "Blue Forces:"))
|
||||
self.blueqty_spinBox.setStatusTip(_translate("MainWindow", "How many groups should we generate?"))
|
||||
self.blueqty_spinBox.setStatusTip(_translate("MainWindow", "Blue vehicle groups per staging or conflict zone."))
|
||||
self.blueforces_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own custom ground forces groups to be automatically generated."))
|
||||
self.scenario_label_4.setText(_translate("MainWindow", "Groups Per Zone"))
|
||||
self.version_label.setText(_translate("MainWindow", "Version string"))
|
||||
@ -594,10 +596,10 @@ class Ui_MainWindow(object):
|
||||
self.scenario_label_7.setText(_translate("MainWindow", "Enemy Attack Helicopters"))
|
||||
self.label_2.setText(_translate("MainWindow", "Player Slots:"))
|
||||
self.scenario_label_9.setText(_translate("MainWindow", "Zone FARP Conditions:"))
|
||||
self.awacs_checkBox.setText(_translate("MainWindow", "Friendly AWACS"))
|
||||
self.awacs_checkBox.setStatusTip(_translate("MainWindow", "Spawn a friendly AWACS with fighter escorts."))
|
||||
self.awacs_checkBox.setText(_translate("MainWindow", "Friendly AWACS with escort"))
|
||||
self.tankers_checkBox.setStatusTip(_translate("MainWindow", "Spawn friendly tankers for both boom and basket refueling."))
|
||||
self.tankers_checkBox.setText(_translate("MainWindow", "Friendly Tankers"))
|
||||
self.inf_spawn_voiceovers_checkBox.setStatusTip(_translate("MainWindow", "Friendly/enemy APCs will drop infantry when reaching a new conflict zone."))
|
||||
self.inf_spawn_voiceovers_checkBox.setText(_translate("MainWindow", "Voiceovers on Infantry Spawn"))
|
||||
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.smoke_pickup_zone_checkBox.setStatusTip(_translate("MainWindow", "Infinite troop pickup zones will be marked with blue smoke."))
|
||||
@ -613,7 +615,8 @@ class Ui_MainWindow(object):
|
||||
self.label_3.setStatusTip(_translate("MainWindow", "The number of troop drops per transport helicopter flight."))
|
||||
self.label_3.setText(_translate("MainWindow", "Transport Drop Points"))
|
||||
self.apcs_spawn_checkBox.setStatusTip(_translate("MainWindow", "Friendly/enemy APCs will drop infantry when reaching a new conflict zone. Disables infinite troop pickups from conflict zones (you must pick up existing troops)."))
|
||||
self.apcs_spawn_checkBox.setText(_translate("MainWindow", "APCs Spawn Infantry"))
|
||||
self.apcs_spawn_checkBox.setText(_translate("MainWindow", "Dynamic Troops"))
|
||||
self.generateButton.setStatusTip(_translate("MainWindow", "Click to generate mission."))
|
||||
self.generateButton.setText(_translate("MainWindow", "GENERATE MISSION"))
|
||||
self.farp_always.setStatusTip(_translate("MainWindow", "Always spawn a FARP in defeated conflict zones."))
|
||||
self.farp_always.setText(_translate("MainWindow", "Always"))
|
||||
@ -623,8 +626,16 @@ class Ui_MainWindow(object):
|
||||
self.farp_gunits.setText(_translate("MainWindow", "20% Ground Units Remaining"))
|
||||
self.nextScenario_pushButton.setText(_translate("MainWindow", ">"))
|
||||
self.prevScenario_pushButton.setText(_translate("MainWindow", "<"))
|
||||
self.menuMap.setTitle(_translate("MainWindow", "Map Filter"))
|
||||
self.menuGametype_Filter.setTitle(_translate("MainWindow", "Gametype Filter"))
|
||||
self.rateButton1.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.hotstart_checkBox.setStatusTip(_translate("MainWindow", "Player helicopters start with engines running on the ground. No effect if player slots says \'Locked to scenario\'"))
|
||||
self.hotstart_checkBox.setText(_translate("MainWindow", "Player Hotstart"))
|
||||
self.rateButton2.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.rateButton3.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.rateButton4.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.rateButton5.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.menuMap.setTitle(_translate("MainWindow", "Map"))
|
||||
self.menuFilter.setTitle(_translate("MainWindow", "Filter"))
|
||||
self.menuPreferences.setTitle(_translate("MainWindow", "Preferences"))
|
||||
self.action_generateMission.setText(_translate("MainWindow", "_generateMission"))
|
||||
self.action_scenarioSelected.setText(_translate("MainWindow", "_scenarioSelected"))
|
||||
self.action_blueforcesSelected.setText(_translate("MainWindow", "_blueforcesSelected"))
|
||||
@ -637,9 +648,28 @@ class Ui_MainWindow(object):
|
||||
self.actionMarianas.setText(_translate("MainWindow", "Marianas"))
|
||||
self.actionNevada.setText(_translate("MainWindow", "Nevada"))
|
||||
self.actionSyria.setText(_translate("MainWindow", "Syria"))
|
||||
self.actionAll.setText(_translate("MainWindow", "All"))
|
||||
self.actionMultiplayer.setText(_translate("MainWindow", "Multiplayer"))
|
||||
self.actionAll_2.setText(_translate("MainWindow", "All"))
|
||||
self.actionSave_Directory.setText(_translate("MainWindow", "Save Directory"))
|
||||
self.action_slotChanged.setText(_translate("MainWindow", "_slotChanged"))
|
||||
self.actionIncluded.setText(_translate("MainWindow", "Included"))
|
||||
self.actionUser.setText(_translate("MainWindow", "User"))
|
||||
self.actionDownloaded.setText(_translate("MainWindow", "Downloaded"))
|
||||
self.action_downloadButton.setText(_translate("MainWindow", "_downloadButton"))
|
||||
self.action_downloadButton.setToolTip(_translate("MainWindow", "_downloadButton"))
|
||||
self.action_rateButton1.setText(_translate("MainWindow", "_rateButton1"))
|
||||
self.action_rateButton1.setToolTip(_translate("MainWindow", "_rateButton1"))
|
||||
self.actionSingle_Player.setText(_translate("MainWindow", "Single-Player"))
|
||||
self.actionCo_Op.setText(_translate("MainWindow", "Co-Op"))
|
||||
self.actionMapMenu.setText(_translate("MainWindow", "actionMapMenu"))
|
||||
self.actionFilterMenu.setText(_translate("MainWindow", "FilterMenu"))
|
||||
self.action_rateButton2.setText(_translate("MainWindow", "_rateButton2"))
|
||||
self.action_rateButton2.setToolTip(_translate("MainWindow", "_rateButton2"))
|
||||
self.action_rateButton3.setText(_translate("MainWindow", "_rateButton3"))
|
||||
self.action_rateButton3.setToolTip(_translate("MainWindow", "_rateButton3"))
|
||||
self.action_rateButton4.setText(_translate("MainWindow", "_rateButton4"))
|
||||
self.action_rateButton4.setToolTip(_translate("MainWindow", "_rateButton4"))
|
||||
self.action_rateButton5.setText(_translate("MainWindow", "_rateButton5"))
|
||||
self.action_rateButton5.setToolTip(_translate("MainWindow", "_rateButton5"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
1380
Generator/MissionGeneratorUI_bkup11.ui
Normal file
88
Generator/MissionGeneratorWeb.py
Normal file
@ -0,0 +1,88 @@
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from MissionGenerator import directories, build, logger
|
||||
import requests
|
||||
import yaml
|
||||
import os
|
||||
|
||||
modules_url = 'https://dcs-helicopters.com/user-files/modules/'
|
||||
version_url = 'https://dcs-helicopters.com/app-updates/versions.yaml'
|
||||
modules_map_url = 'https://dcs-helicopters.com/user-files/modules/modules.yaml'
|
||||
|
||||
def checkVersion(self):
|
||||
try:
|
||||
r = requests.get(version_url, allow_redirects=False, timeout=3)
|
||||
v = yaml.safe_load(r.content)
|
||||
print(v["build"])
|
||||
avail_build = v["build"]
|
||||
if avail_build > build:
|
||||
msg = QMessageBox()
|
||||
msg.setWindowTitle(v["title"])
|
||||
msg.setText(v["description"])
|
||||
x = msg.exec_()
|
||||
except TimeoutError:
|
||||
logger.error("Online version check failed: connection timed out.")
|
||||
except ConnectionError:
|
||||
logger.error("Online version check failed: connection error.")
|
||||
except:
|
||||
logger.error("Online version check failed.")
|
||||
|
||||
|
||||
|
||||
# def loadOnlineContent(self):
|
||||
# url = user_files_url + 'directory.yaml'
|
||||
# r = requests.get(url, allow_redirects=False)
|
||||
# user_files = yaml.safe_load(r.content)
|
||||
# count = 0
|
||||
#
|
||||
# # Download scenarios files
|
||||
# os.chdir(directories.scenarios)
|
||||
# if user_files["scenarios"]["files"]:
|
||||
# for filename in user_files["scenarios"]["files"]:
|
||||
# url = user_files_url + user_files["scenarios"]["dir"] + '/' + filename
|
||||
# r = requests.get(url, allow_redirects=False)
|
||||
# open(filename, 'wb').write(r.content)
|
||||
# count = count + 1
|
||||
#
|
||||
# # Download blue forces files
|
||||
# os.chdir(directories.forces + '/blue')
|
||||
# if user_files["forces_blue"]["files"]:
|
||||
# for filename in user_files["forces_blue"]["files"]:
|
||||
# url = user_files_url + user_files["forces_blue"]["dir"] + '/' + filename
|
||||
# r = requests.get(url, allow_redirects=False)
|
||||
# open(filename, 'wb').write(r.content)
|
||||
# count = count + 1
|
||||
#
|
||||
# # Download red forces files
|
||||
# os.chdir(directories.forces + '/red')
|
||||
# if user_files["forces_red"]["files"]:
|
||||
# for filename in user_files["forces_red"]["files"]:
|
||||
# url = user_files_url + user_files["forces_red"]["dir"] + '/' + filename
|
||||
# r = requests.get(url, allow_redirects=False)
|
||||
# open(filename, 'wb').write(r.content)
|
||||
# count = count + 1
|
||||
#
|
||||
# # Download imports files
|
||||
# os.chdir(directories.imports)
|
||||
# if user_files["imports"]["files"]:
|
||||
# for filename in user_files["imports"]["files"]:
|
||||
# url = user_files_url + user_files["imports"]["dir"] + '/' + filename
|
||||
# r = requests.get(url, allow_redirects=False)
|
||||
# open(filename, 'wb').write(r.content)
|
||||
# count = count + 1
|
||||
#
|
||||
# msg = QMessageBox()
|
||||
# msg.setWindowTitle("Downloaded Files")
|
||||
# msg.setText("We've downloaded " + str(count) + " new files!")
|
||||
# x = msg.exec_()
|
||||
|
||||
# class Module:
|
||||
#
|
||||
# def __init__(self, remote_dir, local_dir):
|
||||
# self.remote_dir = remote_dir
|
||||
# self.local_dir = local_dir
|
||||
#
|
||||
# @classmethod
|
||||
# def createFromFile(cls):
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
Your mission files will appear in this directory after using the mission generator!
|
||||
@ -5,11 +5,13 @@ jtf_red = "Combined Joint Task Forces Red"
|
||||
jtf_blue = "Combined Joint Task Forces Blue"
|
||||
|
||||
|
||||
|
||||
def triggerSetup(rops, 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
|
||||
trig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Scripts")
|
||||
@ -26,7 +28,7 @@ def triggerSetup(rops, options):
|
||||
"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_spawn_messages = " + lb("inf_spawn_msgs") + "\n\n" +
|
||||
"RotorOps.inf_spawn_messages = true\n\n" +
|
||||
"RotorOps.inf_spawns_per_zone = " + lb("inf_spawn_qty") + "\n\n" +
|
||||
"RotorOps.apcs_spawn_infantry = " + lb("apc_spawns_inf") + " \n\n")
|
||||
if not options["smoke_pickup_zones"]:
|
||||
@ -38,7 +40,7 @@ def triggerSetup(rops, options):
|
||||
trig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Zones")
|
||||
trig.rules.append(dcs.condition.TimeAfter(2))
|
||||
for s_zone in rops.staging_zones:
|
||||
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.stagingZone('" + s_zone + "')")))
|
||||
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.addStagingZone('" + s_zone + "')")))
|
||||
for c_zone in rops.conflict_zones:
|
||||
zone_flag = rops.conflict_zones[c_zone].flag
|
||||
trig.actions.append(
|
||||
@ -61,6 +63,15 @@ def triggerSetup(rops, options):
|
||||
z_active_trig.actions.append(dcs.action.DoScript(dcs.action.String("--Add any action you want here!")))
|
||||
rops.m.triggerrules.triggers.append(z_active_trig)
|
||||
|
||||
# # Add CTLD beacons - this might be cool but we'd need to address placement of the 3D objects
|
||||
# trig = dcs.triggers.TriggerOnce(comment="RotorOps CTLD Beacons")
|
||||
# trig.rules.append(dcs.condition.TimeAfter(5))
|
||||
# trig.actions.append(dcs.action.DoScript(dcs.action.String("ctld.createRadioBeaconAtZone('STAGING','blue', 1440,'STAGING/LOGISTICS')")))
|
||||
# for c_zone in rops.conflict_zones:
|
||||
# trig.actions.append(
|
||||
# dcs.action.DoScript(dcs.action.String("ctld.createRadioBeaconAtZone('" + c_zone + "','blue', 1440,'" + c_zone + "')")))
|
||||
# rops.m.triggerrules.triggers.append(trig)
|
||||
|
||||
# Zone protection SAMs
|
||||
if options["zone_protect_sams"]:
|
||||
for index, zone_name in enumerate(rops.conflict_zones):
|
||||
@ -131,11 +142,10 @@ def triggerSetup(rops, options):
|
||||
|
||||
# Add transport helos triggers
|
||||
for index in range(options["e_transport_helos"]):
|
||||
random_zone_index = random.randrange(1, len(rops.conflict_zones))
|
||||
random_zone_obj = list(rops.conflict_zones.items())[random_zone_index]
|
||||
random_zone_obj = random.choice(list(rops.conflict_zones.items()))
|
||||
zone = random_zone_obj[1]
|
||||
z_weak_trig = dcs.triggers.TriggerOnce(comment=zone.name + " Transport Helo")
|
||||
z_weak_trig.rules.append(dcs.condition.FlagEquals(game_flag, random_zone_index + 1))
|
||||
z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone.flag, 1))
|
||||
z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 100)))
|
||||
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String(
|
||||
"---Flag " + str(game_flag) + " value represents the index of the active zone. ")))
|
||||
@ -150,10 +160,13 @@ def triggerSetup(rops, options):
|
||||
trig.rules.append(dcs.condition.FlagEquals(game_flag, 99))
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is WON")))
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("RotorOps.gameMsg(RotorOps.gameMsgs.success)")))
|
||||
rops.m.triggerrules.triggers.append(trig)
|
||||
|
||||
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict LOST")
|
||||
trig.rules.append(dcs.condition.FlagEquals(game_flag, 98))
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is LOST")))
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("RotorOps.gameMsg(RotorOps.gameMsgs.failure)")))
|
||||
rops.m.triggerrules.triggers.append(trig)
|
||||
@ -9,6 +9,7 @@ import RotorOpsGroups
|
||||
import RotorOpsUnits
|
||||
import RotorOpsUtils
|
||||
import RotorOpsConflict
|
||||
import aircraftMods
|
||||
from RotorOpsImport import ImportObjects
|
||||
import time
|
||||
from MissionGenerator import logger
|
||||
@ -21,15 +22,6 @@ class RotorOpsMission:
|
||||
|
||||
def __init__(self):
|
||||
self.m = dcs.mission.Mission()
|
||||
# os.chdir("../")
|
||||
# directories.home_dir = os.getcwd()
|
||||
# directories.scenarios = directories.home_dir + "\Generator\Scenarios"
|
||||
# directories.forces = directories.home_dir + "\Generator\Forces"
|
||||
# directories.scripts = directories.home_dir
|
||||
# directories.sound = directories.home_dir + "\sound\embedded"
|
||||
# directories.output = directories.home_dir + "\Generator\Output"
|
||||
# directories.assets = directories.home_dir + "\Generator/assets"
|
||||
# directories.imports = directories.home_dir + "\Generator\Imports"
|
||||
|
||||
self.conflict_zones = {}
|
||||
self.staging_zones = {}
|
||||
@ -38,6 +30,7 @@ class RotorOpsMission:
|
||||
self.res_map = {}
|
||||
self.config = None
|
||||
|
||||
|
||||
class RotorOpsZone:
|
||||
def __init__(self, name: str, flag: int, position: dcs.point, size: int):
|
||||
self.name = name
|
||||
@ -82,7 +75,7 @@ class RotorOpsMission:
|
||||
logger.info("Adding script to mission: " + filename)
|
||||
self.scripts[filename] = self.m.map_resource.add_resource_file(filename)
|
||||
|
||||
def getUnitsFromMiz(self, filename, side):
|
||||
def getUnitsFromMiz(self, file, side='both'):
|
||||
|
||||
forces = {}
|
||||
vehicles = []
|
||||
@ -90,49 +83,53 @@ class RotorOpsMission:
|
||||
transport_helos = []
|
||||
attack_planes = []
|
||||
fighter_planes = []
|
||||
helicopters = []
|
||||
|
||||
os.chdir(directories.home_dir)
|
||||
os.chdir(directories.forces + "/" + side)
|
||||
logger.info("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:
|
||||
vehicles.append(vehicle_group)
|
||||
for helicopter_group in country_obj.helicopter_group:
|
||||
if helicopter_group.task == 'CAS':
|
||||
attack_helos.append(helicopter_group)
|
||||
elif helicopter_group.task == 'Transport':
|
||||
transport_helos.append(helicopter_group)
|
||||
for plane_group in country_obj.plane_group:
|
||||
if plane_group.task == 'CAS':
|
||||
attack_planes.append(plane_group)
|
||||
elif plane_group.task == 'CAP':
|
||||
fighter_planes.append(plane_group)
|
||||
source_mission.load_file(file)
|
||||
if side == 'both':
|
||||
sides = ['red', 'blue']
|
||||
else:
|
||||
sides = [side]
|
||||
for side in sides:
|
||||
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:
|
||||
vehicles.append(vehicle_group)
|
||||
for helicopter_group in country_obj.helicopter_group:
|
||||
helicopters.append(helicopter_group)
|
||||
if helicopter_group.task == 'CAS':
|
||||
attack_helos.append(helicopter_group)
|
||||
elif helicopter_group.task == 'Transport':
|
||||
transport_helos.append(helicopter_group)
|
||||
for plane_group in country_obj.plane_group:
|
||||
if plane_group.task == 'CAS':
|
||||
attack_planes.append(plane_group)
|
||||
elif plane_group.task == 'CAP':
|
||||
fighter_planes.append(plane_group)
|
||||
|
||||
forces["vehicles"] = vehicles
|
||||
forces["attack_helos"] = attack_helos
|
||||
forces["transport_helos"] = transport_helos
|
||||
forces["attack_planes"] = attack_planes
|
||||
forces["fighter_planes"] = fighter_planes
|
||||
forces["helicopters"] = helicopters
|
||||
|
||||
return forces
|
||||
|
||||
except:
|
||||
logger.error("Failed to load units from " + filename)
|
||||
logger.error("Failed to load units from " + file)
|
||||
|
||||
def generateMission(self, window, options):
|
||||
|
||||
def generateMission(self, options):
|
||||
os.chdir(directories.scenarios)
|
||||
logger.info("Looking for mission files in " + os.getcwd())
|
||||
|
||||
|
||||
|
||||
self.m.load_file(options["scenario_filename"])
|
||||
|
||||
window.statusBar().showMessage("Loading scenario mission", 10000)
|
||||
self.m.load_file(options["scenario_file"])
|
||||
self.addMods()
|
||||
self.importObjects()
|
||||
|
||||
#todo: test
|
||||
@ -142,8 +139,10 @@ class RotorOpsMission:
|
||||
failure_msg = "You must include a CombinedJointTaskForcesBlue and CombinedJointTaskForcesRed unit in the scenario template. See the instructions in " + directories.scenarios
|
||||
return {"success": False, "failure_msg": failure_msg}
|
||||
|
||||
red_forces = self.getUnitsFromMiz(options["red_forces_filename"], "red")
|
||||
blue_forces = self.getUnitsFromMiz(options["blue_forces_filename"], "blue")
|
||||
# red_forces = self.getUnitsFromMiz(directories.forces + "/red/" + options["red_forces_filename"], "red")
|
||||
# blue_forces = self.getUnitsFromMiz(directories.forces + "/blue/" + options["blue_forces_filename"], "blue")
|
||||
red_forces = self.getUnitsFromMiz(directories.forces + "/" + options["red_forces_filename"], "both")
|
||||
blue_forces = self.getUnitsFromMiz(directories.forces + "/" + options["blue_forces_filename"], "both")
|
||||
|
||||
# Add coalitions (we may be able to add CJTF here instead of requiring templates to have objects of those coalitions)
|
||||
self.m.coalition.get("red").add_country(dcs.countries.Russia())
|
||||
@ -183,6 +182,7 @@ class RotorOpsMission:
|
||||
|
||||
|
||||
#Populate Red zones with ground units
|
||||
window.statusBar().showMessage("Populating units into mission...", 10000)
|
||||
|
||||
for zone_name in red_zones:
|
||||
if red_forces["vehicles"]:
|
||||
@ -249,10 +249,7 @@ class RotorOpsMission:
|
||||
180, zone_name + " FARP", late_activation=False)
|
||||
|
||||
#add logistics sites
|
||||
if options["crates"] and zone_name in self.staging_zones:
|
||||
# RotorOpsGroups.VehicleTemplate.CombinedJointTaskForcesBlue.logistics_site(self.m, self.m.country(jtf_blue),
|
||||
# blue_zones[zone_name].position,
|
||||
# 180, zone_name)
|
||||
if options["crates"] and zone_name == "STAGING":
|
||||
os.chdir(directories.imports)
|
||||
staging_flag = self.m.find_group(zone_name)
|
||||
if staging_flag:
|
||||
@ -266,13 +263,6 @@ class RotorOpsMission:
|
||||
i.copyAll(self.m, jtf_blue, "Staging Logistics Zone",
|
||||
staging_position, staging_heading)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if options["zone_protect_sams"] and options["defending"]:
|
||||
vg = self.m.vehicle_group(
|
||||
self.m.country(jtf_blue),
|
||||
@ -286,6 +276,7 @@ class RotorOpsMission:
|
||||
|
||||
|
||||
#Add player slots
|
||||
window.statusBar().showMessage("Adding flights to mission...", 10000)
|
||||
if options["slots"] != "Locked to Scenario" and options["slots"] != "None":
|
||||
self.addPlayerHelos(options)
|
||||
|
||||
@ -297,15 +288,22 @@ class RotorOpsMission:
|
||||
self.m.map.zoom = 100000
|
||||
|
||||
#add files and triggers necessary for RotorOps.lua script
|
||||
|
||||
window.statusBar().showMessage("Adding resources to mission...", 10000)
|
||||
self.addResources(directories.sound, directories.scripts)
|
||||
RotorOpsConflict.triggerSetup(self, options)
|
||||
|
||||
|
||||
#Save the mission file
|
||||
os.chdir(directories.output)
|
||||
output_filename = options["scenario_filename"].removesuffix('.miz') + " " + time.strftime('%a%H%M%S') + '.miz'
|
||||
window.statusBar().showMessage("Saving mission...", 10000)
|
||||
if window.user_output_dir:
|
||||
output_dir = window.user_output_dir # if user has set output dir
|
||||
else:
|
||||
output_dir = directories.output # default dir
|
||||
os.chdir(output_dir)
|
||||
output_filename = options["scenario_name"] + " " + time.strftime('%a%H%M%S') + '.miz'
|
||||
success = self.m.save(output_filename)
|
||||
return {"success": success, "filename": output_filename, "directory": directories.output} #let the UI know the result
|
||||
return {"success": success, "filename": output_filename, "directory": output_dir} #let the UI know the result
|
||||
|
||||
def addGroundGroups(self, zone, _country, groups, quantity):
|
||||
for a in range(0, quantity):
|
||||
@ -459,7 +457,16 @@ class RotorOpsMission:
|
||||
client_helos = RotorOpsUnits.client_helos
|
||||
for helicopter in dcs.helicopters.helicopter_map:
|
||||
if helicopter == options["slots"]:
|
||||
client_helos = [dcs.helicopters.helicopter_map[helicopter]]
|
||||
client_helos = [dcs.helicopters.helicopter_map[helicopter]] #if out ui slot option matches a specific helicopter type name
|
||||
|
||||
# get loadouts from miz file and put into a simple dict
|
||||
default_loadouts = {}
|
||||
default_unit_groups = self.getUnitsFromMiz(directories.home_dir + "\\config\\blue_player_loadouts.miz", "blue")
|
||||
for helicopter_group in default_unit_groups["helicopters"]:
|
||||
default_loadouts[helicopter_group.units[0].unit_type.id] = {}
|
||||
default_loadouts[helicopter_group.units[0].unit_type.id]["pylons"] = helicopter_group.units[0].pylons
|
||||
default_loadouts[helicopter_group.units[0].unit_type.id]["livery_id"] = helicopter_group.units[0].livery_id
|
||||
default_loadouts[helicopter_group.units[0].unit_type.id]["fuel"] = helicopter_group.units[0].fuel
|
||||
|
||||
#find friendly carriers and farps
|
||||
carrier = self.m.country(jtf_blue).find_ship_group(name="HELO_CARRIER")
|
||||
@ -474,16 +481,33 @@ class RotorOpsMission:
|
||||
|
||||
heading = 0
|
||||
group_size = 1
|
||||
player_helicopters = []
|
||||
if options["slots"] == "Multiple Slots":
|
||||
player_helicopters = options["player_slots"]
|
||||
else:
|
||||
player_helicopters.append(options["slots"]) # single helicopter type
|
||||
|
||||
if len(client_helos) == 1:
|
||||
group_size = 2 #add a wingman if singleplayer
|
||||
|
||||
for helotype in client_helos:
|
||||
start_type = dcs.mission.StartType.Cold
|
||||
if options["player_hotstart"]:
|
||||
start_type = dcs.mission.StartType.Warm
|
||||
|
||||
farp_helicopter_count = 1
|
||||
for helicopter_id in player_helicopters:
|
||||
helotype = None
|
||||
if helicopter_id in dcs.helicopters.helicopter_map:
|
||||
helotype = dcs.helicopters.helicopter_map[helicopter_id]
|
||||
else:
|
||||
continue
|
||||
if carrier:
|
||||
fg = self.m.flight_group_from_unit(self.m.country(jtf_blue), "CARRIER " + helotype.id, helotype, carrier,
|
||||
dcs.task.CAS, group_size=group_size)
|
||||
elif farp:
|
||||
dcs.task.CAS, group_size=group_size, start_type=start_type)
|
||||
elif farp and farp_helicopter_count <= 4:
|
||||
farp_helicopter_count = farp_helicopter_count + 1
|
||||
fg = self.m.flight_group_from_unit(self.m.country(jtf_blue), "FARP " + helotype.id, helotype, farp,
|
||||
dcs.task.CAS, group_size=group_size)
|
||||
dcs.task.CAS, group_size=group_size, start_type=start_type)
|
||||
|
||||
#invisible farps need manual unit placement for multiple units
|
||||
if farp.units[0].type == 'Invisible FARP':
|
||||
@ -493,13 +517,20 @@ class RotorOpsMission:
|
||||
heading += 90
|
||||
else:
|
||||
fg = self.m.flight_group_from_airport(self.m.country(jtf_blue), primary_f_airport.name + " " + helotype.id, helotype,
|
||||
self.getParking(primary_f_airport, helotype), group_size=group_size)
|
||||
self.getParking(primary_f_airport, helotype), group_size=group_size, start_type=start_type)
|
||||
fg.units[0].set_client()
|
||||
fg.load_task_default_loadout(dcs.task.CAS)
|
||||
#fg.load_task_default_loadout(dcs.task.CAS)
|
||||
if helotype.id in default_loadouts:
|
||||
fg.units[0].pylons = default_loadouts[helotype.id]["pylons"]
|
||||
fg.units[0].livery_id = default_loadouts[helotype.id]["livery_id"]
|
||||
fg.units[0].fuel = default_loadouts[helotype.id]["fuel"]
|
||||
|
||||
#setup wingman for single player
|
||||
if len(fg.units) == 2:
|
||||
fg.units[1].skill = dcs.unit.Skill.High
|
||||
fg.units[1].pylons = fg.units[0].pylons
|
||||
fg.units[1].livery_id = fg.units[0].livery_id
|
||||
fg.units[1].fuel = fg.units[0].fuel
|
||||
|
||||
|
||||
class TrainingScenario():
|
||||
@ -683,7 +714,7 @@ class RotorOpsMission:
|
||||
farp,
|
||||
maintask=dcs.task.CAS,
|
||||
start_type=dcs.mission.StartType.Cold,
|
||||
group_size=group_size)
|
||||
group_size=1) # more than one spawn on top of each other, setting group size to one for now
|
||||
zone_attack(afg, farp)
|
||||
|
||||
elif airport:
|
||||
@ -794,3 +825,6 @@ class RotorOpsMission:
|
||||
i.anchorByGroupName("ANCHOR")
|
||||
new_statics, new_vehicles, new_helicopters = i.copyAll(self.m, country_name, group.units[0].name, group.units[0].position, group.units[0].heading)
|
||||
|
||||
def addMods(self):
|
||||
dcs.helicopters.helicopter_map["UH-60L"] = aircraftMods.UH_60L
|
||||
self.m.country(jtf_blue).helicopters.append(aircraftMods.UH_60L)
|
||||
|
||||
@ -1,12 +1,27 @@
|
||||
import dcs
|
||||
import aircraftMods
|
||||
|
||||
client_helos = [
|
||||
dcs.helicopters.UH_1H,
|
||||
#aircraftMods.UH_60L,
|
||||
dcs.helicopters.AH_64D_BLK_II,
|
||||
dcs.helicopters.Mi_24P,
|
||||
dcs.helicopters.Ka_50,
|
||||
]
|
||||
|
||||
player_helos = [
|
||||
dcs.helicopters.AH_64D_BLK_II,
|
||||
dcs.helicopters.Ka_50,
|
||||
dcs.helicopters.Mi_8MT,
|
||||
dcs.helicopters.Mi_24P,
|
||||
dcs.helicopters.SA342M,
|
||||
dcs.helicopters.SA342L,
|
||||
dcs.helicopters.SA342Minigun,
|
||||
dcs.helicopters.SA342Mistral,
|
||||
dcs.helicopters.UH_1H,
|
||||
aircraftMods.UH_60L,
|
||||
]
|
||||
|
||||
e_attack_helos = [
|
||||
dcs.helicopters.Mi_24P,
|
||||
dcs.helicopters.Ka_50,
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
You can add your own scenarios in this directory and they will appear in the mission generator. See the other scenario .miz files for examples of what to include in your template.
|
||||
|
||||
|
||||
A scenario .miz file MUST have:
|
||||
|
||||
1) Between 1-4 circular 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/on-side your staging zone).
|
||||
4) A red airport (recommend somewhere near/on-side your last conflict zone).
|
||||
5) At least one Russian unit or static object. Anything will work, even a cow. You can set "HIDDEN ON MAP".
|
||||
6) At least one USA unit or static object. See previous point.
|
||||
|
||||
|
||||
|
||||
Optional:
|
||||
7) USA FARP called "HELO_FARP" for automatic player placement. (Strongly suggest using immortal and invisible options for support vehicles, and add "static" to the group name to keep them from moving)
|
||||
8) USA Carrier called "HELO_CARRIER" for automatic player placement.
|
||||
9) Infantry spawn zones inside conflict zones. Add near buildings to simulate infantry hiding within. Name trigger zones like "ALPHA_SPAWN", "ALPHA_SPAWN_2, etc.
|
||||
|
||||
Testing:
|
||||
You should test your scenarios to ensure that vehicals move between zones as you expect. In some circumstances, vehicles may not be able to calculate a valid route to the next zone, and they will stop. Make sure they can route correctly by testing your scenario in fast forward, with few defending units. You can also get a good idea as to how long your scenario will take to complete.
|
||||
|
||||
|
||||
Tips:
|
||||
-Position the center of conflict zones over an open area, as this position will be used to spawn units and FARPs.
|
||||
-Position the center of staging zones over an open area of at least 1000 x 1000ft to provide space for logistics zones.
|
||||
-For very scenery dense areas like forests or urban areas, smaller conflict zone sizes are highly recommended (eg 4000ft radius). A zone center near a roadway may also help keep units moving smoothly.
|
||||
-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 for a rearming area, or perhaps unarmed client slots.
|
||||
-Player/client slots are placed in this priority order: Carrier named "HELO_CARRIER", FARP named "HELO_FARP", and the blue airfield.
|
||||
-Friendly AWACs and tankers will be placed at the blue airport if parking is available, otherwise they will start in the air.
|
||||
-Enemy helicopters and planes will spawn at the red airport.
|
||||
-Late activation FARPs might be useful for rearming far from the player spawn point.
|
||||
-In "Defense" or with "Swap sides" option, USA and Russia ships, helicopters, planes, and ground units will swap countries. Static objects may not. Test it out.
|
||||
-Turn off or limit civilian road traffic.
|
||||
-Pay attention to rivers and bridges, as a far away bridge crossing may break routing if it's the only way across. See the testing notes above.
|
||||
|
||||
v0.6:
|
||||
You can now control the FARP spawning location and heading by adding a static object (such as a mark flag) with group name 'ALPHA' etc. Same for staging area logistics site..but the group name should be 'STAGING'. Change the object heading to better align with roads or map objects. The flags for these must be CJTFB country.
|
||||
|
||||
You can dynamically insert complex arangements of objects such as large bases with multiplayer spawns. See the 'Imports' folder for more details.
|
||||
23
Generator/aircraftMods.py
Normal file
@ -0,0 +1,23 @@
|
||||
import dcs.task as task
|
||||
from dcs.helicopters import HelicopterType
|
||||
from typing import Set
|
||||
|
||||
# RotorOps class for UH-60L mod
|
||||
class UH_60L(HelicopterType):
|
||||
id = "UH-60L"
|
||||
flyable = True
|
||||
height = 5.13
|
||||
width = 16.4
|
||||
length = 19.76
|
||||
fuel_max = 1362
|
||||
max_speed = 300
|
||||
chaff = 30
|
||||
flare = 60
|
||||
charge_total = 90
|
||||
chaff_charge_size = 1
|
||||
flare_charge_size = 1
|
||||
|
||||
pylons: Set[int] = {1, 2, 3, 4, 5, 6, 7}
|
||||
|
||||
tasks = [task.Transport]
|
||||
task_default = task.Transport
|
||||
|
Before Width: | Height: | Size: 1.5 MiB |
@ -1,5 +1,8 @@
|
||||
#build UI files
|
||||
pyuic5 -x MissionGeneratorUI.ui -o MissionGeneratorUI.py
|
||||
|
||||
#build resources
|
||||
pyrcc5 -o resources.py resources.qrc
|
||||
|
||||
#build exe
|
||||
pyinstaller MissionGenerator.spec --distpath ..\ -i='assets\icon.ico'
|
||||
BIN
Generator/install-config.ifp
Normal file
34
Generator/installforge_constants.txt
Normal file
@ -0,0 +1,34 @@
|
||||
for reference from here: https://installforge.net/forums/viewtopic.php?t=2
|
||||
|
||||
<InstallPath>
|
||||
|
||||
<AppName>
|
||||
<ProgramVersion>
|
||||
<Company>
|
||||
<Website>
|
||||
<Language>
|
||||
<Username>
|
||||
<Organisation>
|
||||
<Serial>
|
||||
|
||||
<Windows>
|
||||
<System>
|
||||
<Temp>
|
||||
<Font>
|
||||
|
||||
<ProgramFiles>
|
||||
|
||||
<Desktop>
|
||||
<DesktopPublic>
|
||||
|
||||
<Startup>
|
||||
<StartupPublic>
|
||||
|
||||
<Documents>
|
||||
<DocumentsPublic>
|
||||
|
||||
<StartmenuPrograms>
|
||||
<StartmenuProgramsPublic>
|
||||
|
||||
<AppData>
|
||||
<AppDataPublic>
|
||||
1197
Generator/resources.py
Normal file
6
Generator/resources.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/images">
|
||||
<file alias="star_full">resources/star_full.png</file>
|
||||
<file alias="star_empty">resources/star_empty.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
BIN
Generator/resources/star_empty.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
Generator/resources/star_full.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
41
Generator/user.py
Normal file
@ -0,0 +1,41 @@
|
||||
import secrets
|
||||
import os
|
||||
import winreg
|
||||
|
||||
|
||||
path = winreg.HKEY_CURRENT_USER
|
||||
|
||||
def saveReg(k,v):
|
||||
try:
|
||||
key = winreg.OpenKeyEx(path, r"SOFTWARE\\")
|
||||
newKey = winreg.CreateKey(key,"RotorOps")
|
||||
winreg.SetValueEx(newKey, k, 0, winreg.REG_SZ, str(v))
|
||||
if newKey:
|
||||
winreg.CloseKey(newKey)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
|
||||
def readReg(k):
|
||||
try:
|
||||
key = winreg.OpenKeyEx(path, r"SOFTWARE\\RotorOps\\")
|
||||
value = winreg.QueryValueEx(key,k)
|
||||
if key:
|
||||
winreg.CloseKey(key)
|
||||
return value[0]
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return None
|
||||
|
||||
def createUserKey():
|
||||
userid = readReg('User')
|
||||
if not userid or userid == 'None':
|
||||
print("Unable to find userid in registry.")
|
||||
userid = secrets.token_urlsafe(10)
|
||||
if saveReg('User', userid):
|
||||
print("Saved userid to registry")
|
||||
return userid
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||

|
||||

|
||||
|
||||
# What is RotorOps?
|
||||
RotorOps is a mission generator and gameplay script for DCS: World. At its heart is a game type called Conflict, which requires helicopter operations to win battles on the ground. This is a territory-capture game that promotes focus on individual 'conflict zones'.
|
||||
|
||||
At the core of the RotorOps script are AI enhancements that provide a dynamic ground war by causing automatic conflicts between ground forces and a progression of the front line.
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
# Key Features:
|
||||
|
||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
BIN
assets/briefing1.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 689 KiB After Width: | Height: | Size: 689 KiB |
|
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
BIN
assets/splash.jpg
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
assets/star_empty.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/star_full.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
config/blue_player_loadouts.miz
Normal file
29
config/default-config.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
description:
|
||||
player_spawn: auto
|
||||
checkboxes:
|
||||
defense_checkBox: false
|
||||
apcs_spawn_checkBox: true
|
||||
logistics_crates_checkBox: true
|
||||
awacs_checkBox: true
|
||||
tankers_checkBox: true
|
||||
zone_sams_checkBox: false
|
||||
smoke_pickup_zone_checkBox: false
|
||||
inf_spawn_voiceovers_checkBox: true
|
||||
game_status_checkBox: true
|
||||
voiceovers_checkBox: true
|
||||
force_offroad_checkBox: false
|
||||
hotstart_checkBox: false
|
||||
disable_checkboxes:
|
||||
spinboxes:
|
||||
blueqty_spinBox: 4
|
||||
redqty_spinBox: 2
|
||||
e_attack_helos_spinBox: 2
|
||||
e_attack_planes_spinBox: 1
|
||||
e_transport_helos_spinBox: 1
|
||||
troop_drop_spinBox: 4
|
||||
inf_spawn_spinBox: 2
|
||||
radiobuttons: farp_gunits,
|
||||
red_forces: "RED Default Armor (HARD)"
|
||||
blue_forces: "BLUE Default US Armor"
|
||||
|
||||
|
||||
2
config/user-data.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
local_ratings:
|
||||
C:\RotorOps\templates\Scenarios\included\007d3d\Nevada Conflict - Vegas Tour (GRIMM).miz: 4
|
||||
@ -1 +0,0 @@
|
||||
Use of this software or derivitives of this software is not permitted on 24/7 public multiplayer servers without permission. Scheduled events are excepted.
|
||||
6427
scripts/CTLD2.lua
Normal file
@ -1,5 +1,5 @@
|
||||
RotorOps = {}
|
||||
RotorOps.version = "1.2.7"
|
||||
RotorOps.version = "1.2.8"
|
||||
local debug = true
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@ RotorOps.CTLD_crates = false
|
||||
RotorOps.CTLD_sound_effects = true --sound effects for troop pickup/dropoffs
|
||||
RotorOps.exclude_ai_group_name = "Static" --include this somewhere in a group name to exclude the group from being tasked in the active zone
|
||||
RotorOps.pickup_zone_smoke = "blue"
|
||||
RotorOps.apc_group = {mg=1,at=0,aa=0,inf=3,mortar=0} --not used yet, but we should define the CTLD groups
|
||||
|
||||
|
||||
---[[END OF OPTIONS]]---
|
||||
@ -45,7 +46,7 @@ RotorOps.zones = {}
|
||||
RotorOps.active_zone = "" --name of the active zone
|
||||
RotorOps.active_zone_index = 0
|
||||
RotorOps.game_state_flag = 1 --user flag to store the game state
|
||||
RotorOps.staging_zone = ""
|
||||
RotorOps.staging_zones = {}
|
||||
RotorOps.ctld_pickup_zones = {} --keep track of ctld zones we've added, mainly for map markup
|
||||
RotorOps.ai_defending_infantry_groups = {}
|
||||
RotorOps.ai_attacking_infantry_groups = {}
|
||||
@ -59,6 +60,7 @@ trigger.action.outText("ROTOR OPS STARTED: "..RotorOps.version, 5)
|
||||
env.info("ROTOR OPS STARTED: "..RotorOps.version)
|
||||
|
||||
RotorOps.staged_units = {} --table of ground units that started in the staging zone
|
||||
RotorOps.staged_units_by_zone = {}
|
||||
RotorOps.eventHandler = {}
|
||||
local commandDB = {}
|
||||
local game_message_buffer = {}
|
||||
@ -200,7 +202,7 @@ function RotorOps.eventHandler:onEvent(event)
|
||||
if (world.event.S_EVENT_ENGINE_STARTUP == event.id) then --play some sound files when a player starts engines
|
||||
local initiator = event.initiator:getGroup():getID()
|
||||
|
||||
if #event.initiator:getGroup():getUnits() == 1 then --if there are no other units in the player flight group (preventing duplicated messages for ai wingman flights)
|
||||
if #event.initiator:getGroup():getUnits() == 1 and RotorOps.voice_overs then --if there are no other units in the player flight group (preventing duplicated messages for ai wingman flights)
|
||||
if RotorOps.defending then
|
||||
trigger.action.outSoundForGroup(initiator , RotorOps.gameMsgs.enemy_pushing[RotorOps.active_zone_index + 1][2])
|
||||
else
|
||||
@ -423,7 +425,12 @@ function RotorOps.getValidUnitFromGroup(grp)
|
||||
else
|
||||
group_obj = grp
|
||||
end
|
||||
if grp:isExist() ~= true then return nil end
|
||||
if not grp then
|
||||
return nil
|
||||
end
|
||||
if grp:isExist() ~= true then
|
||||
return nil
|
||||
end
|
||||
local first_valid_unit
|
||||
for index, unit in pairs(grp:getUnits())
|
||||
do
|
||||
@ -521,6 +528,36 @@ function RotorOps.aiTask(grp, task, zone)
|
||||
end
|
||||
|
||||
|
||||
--add units to the staged_units table for ai tasking as attackers
|
||||
function RotorOps.tallyZone(zone_name)
|
||||
local new_units
|
||||
if RotorOps.defending then
|
||||
new_units = mist.getUnitsInZones(mist.makeUnitTable({'[red][vehicle]'}), {zone_name})
|
||||
else
|
||||
new_units = mist.getUnitsInZones(mist.makeUnitTable({'[blue][vehicle]'}), {zone_name})
|
||||
end
|
||||
|
||||
if new_units and #new_units > 0 then
|
||||
|
||||
for index, unit in pairs(new_units) do
|
||||
if not hasValue(RotorOps.staged_units, unit) then
|
||||
env.info("RotorOps adding new units to staged_units: "..#new_units)
|
||||
table.insert(RotorOps.staged_units, unit)
|
||||
RotorOps.aiTask(unit:getGroup(),"move_to_active_zone", RotorOps.zones[RotorOps.active_zone_index].name)
|
||||
else
|
||||
--env.info("unit already in table")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
--
|
||||
-- for index, unit in pairs(RotorOps.staged_units) do
|
||||
-- if string.find(Unit.getGroup(unit):getName():lower(), RotorOps.exclude_ai_group_name:lower()) then
|
||||
-- RotorOps.staged_units[index] = nil --remove 'static' units
|
||||
-- end
|
||||
-- end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---AI CORE BEHAVIOR--
|
||||
@ -976,11 +1013,9 @@ function RotorOps.assessUnitsInZone(var)
|
||||
if RotorOps.defending == true then
|
||||
RotorOps.game_state = RotorOps.game_states.lost
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.lost)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.failure)
|
||||
else
|
||||
RotorOps.game_state = RotorOps.game_states.won
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.won)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.success)
|
||||
end
|
||||
return --we won't reset our timer to fire this function again
|
||||
end
|
||||
@ -995,7 +1030,6 @@ function RotorOps.assessUnitsInZone(var)
|
||||
if RotorOps.defending and defending_game_won then
|
||||
RotorOps.game_state = RotorOps.game_states.won
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.won)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.success)
|
||||
return --we won't reset our timer to fire this function again
|
||||
end
|
||||
|
||||
@ -1193,7 +1227,6 @@ function RotorOps.setActiveZone(new_index)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.enemy_pushing, new_index)
|
||||
else
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.push, new_index)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.get_troops_to_zone, new_index)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1281,13 +1314,14 @@ function RotorOps.addZone(_name, _zone_defenders_flag)
|
||||
RotorOps.addPickupZone(_name, RotorOps.pickup_zone_smoke, -1, "no", 2)
|
||||
end
|
||||
|
||||
function RotorOps.stagingZone(_name)
|
||||
|
||||
function RotorOps.addStagingZone(_name)
|
||||
if trigger.misc.getZone(_name) == nil then
|
||||
trigger.action.outText(_name.." trigger zone missing! Check RotorOps setup!", 60)
|
||||
env.warning(_name.." trigger zone missing! Check RotorOps setup!")
|
||||
end
|
||||
RotorOps.addPickupZone(_name, RotorOps.pickup_zone_smoke, -1, "no", 0)
|
||||
RotorOps.staging_zone = _name
|
||||
RotorOps.staging_zones[#RotorOps.staging_zones + 1] = _name
|
||||
end
|
||||
|
||||
|
||||
@ -1325,17 +1359,26 @@ function RotorOps.addPickupZone(zone_name, smoke, limit, active, side)
|
||||
RotorOps.ctld_pickup_zones[#RotorOps.ctld_pickup_zones + 1] = zone_name
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = {zone_name, smoke, limit, active, side}
|
||||
end
|
||||
|
||||
|
||||
|
||||
function RotorOps.startConflict()
|
||||
if RotorOps.game_state ~= RotorOps.game_states.not_started then return end
|
||||
--if RotorOps.game_state ~= RotorOps.game_states.not_started then return end
|
||||
|
||||
--make some changes to the radio menu
|
||||
--local conflict_zones_menu = commandDB['conflict_zones_menu']
|
||||
--missionCommands.removeItem(commandDB['start_conflict'])
|
||||
--commandDB['clear_zone'] = missionCommands.addCommand( "[CHEAT] Force Clear Zone" , conflict_zones_menu , RotorOps.clearActiveZone)
|
||||
|
||||
RotorOps.staged_units = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), {RotorOps.staging_zone})
|
||||
|
||||
RotorOps.staged_units = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), RotorOps.staging_zones)
|
||||
|
||||
--filter out 'static' units
|
||||
-- for index, unit in pairs(RotorOps.staged_units) do
|
||||
-- if string.find(Unit.getGroup(unit):getName():lower(), RotorOps.exclude_ai_group_name:lower()) then
|
||||
-- RotorOps.staged_units[index] = nil --remove 'static' units
|
||||
-- end
|
||||
-- end
|
||||
|
||||
|
||||
if RotorOps.staged_units[1] == nil then
|
||||
trigger.action.outText("RotorOps failed: You must place ground units in the staging and conflict zones!" , 60, false)
|
||||
@ -1347,10 +1390,16 @@ function RotorOps.startConflict()
|
||||
RotorOps.defending = true
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.start_defense)
|
||||
ctld.activatePickupZone(RotorOps.zones[#RotorOps.zones].name) --make the last zone a pickup zone for defenders
|
||||
ctld.deactivatePickupZone(RotorOps.staging_zone)
|
||||
for index, zone in pairs(RotorOps.staging_zones) do
|
||||
ctld.deactivatePickupZone(zone)
|
||||
end
|
||||
|
||||
else
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.start)
|
||||
ctld.activatePickupZone(RotorOps.staging_zone)
|
||||
for index, zone in pairs(RotorOps.staging_zones) do
|
||||
ctld.activatePickupZone(zone)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
RotorOps.setActiveZone(1)
|
||||
66
server/user-files/modules/mapscript.py
Normal file
@ -0,0 +1,66 @@
|
||||
# A script for creating the modules map file
|
||||
|
||||
import os
|
||||
import yaml
|
||||
|
||||
print("Current dir: " + os.getcwd())
|
||||
modules = []
|
||||
module_folders = next(os.walk('.'))[1]
|
||||
for folder in module_folders:
|
||||
|
||||
valid_module = False
|
||||
module_filenames = []
|
||||
module = {}
|
||||
print("searching folder: " + folder)
|
||||
|
||||
for filename in os.listdir(folder):
|
||||
module_filenames.append(filename)
|
||||
|
||||
# assume the yaml file is our scenario configuration file
|
||||
if filename.endswith(".yaml"):
|
||||
#print("found config file: " + filename)
|
||||
stream = file(os.path.join(folder, filename), 'r')
|
||||
config = yaml.load(stream)
|
||||
#print("Config file yaml: " + str(config))
|
||||
|
||||
if 'name' in config:
|
||||
print("Config file has name: " + config['name'])
|
||||
valid_module = True
|
||||
module['name'] = config['name']
|
||||
|
||||
if valid_module:
|
||||
print("Populating module attributes for " + folder)
|
||||
module['id'] = folder
|
||||
module['dist'] = 'add'
|
||||
module['path'] = 'templates\Scenarios\downloaded'
|
||||
module['files'] = module_filenames
|
||||
|
||||
if 'version' in config:
|
||||
module['version'] = config['version']
|
||||
else:
|
||||
module['version'] = 1
|
||||
|
||||
if 'requires' in config:
|
||||
module['requires'] = config['requires']
|
||||
else:
|
||||
module['requires'] = 1
|
||||
|
||||
modules.append(module)
|
||||
|
||||
print("Found modules: " + str(len(modules)))
|
||||
|
||||
if len(modules) > 0:
|
||||
modulemap = {}
|
||||
#print(str(modules))
|
||||
for m in modules:
|
||||
print("adding module: " + m["id"])
|
||||
modulemap[m['id']] = m
|
||||
|
||||
with open('module-map.yaml', 'w') as mapfile:
|
||||
print("Creating map file...")
|
||||
yaml.dump(modulemap, mapfile)
|
||||
print("Success.")
|
||||
|
||||
|
||||
|
||||
|
||||
24
templates/Forces/_How to add your own templates.txt
Normal file
@ -0,0 +1,24 @@
|
||||
## Forces Templates
|
||||
|
||||
The friendly/enemy forces templates available in the generator are simply .miz files in the Generator/Forces folder.
|
||||
|
||||
A Forces template defines the groups of ground units available, AI aircraft, liveries, and loadouts.
|
||||
|
||||
To create your own Forces template:
|
||||
|
||||
1) Create an empty mission on Caucasus
|
||||
2) Add ground unit groups.
|
||||
3) Save the mission in this directory.
|
||||
|
||||
Optional:
|
||||
|
||||
4) Add helicopters with "CAS" main task for attack helicopters.
|
||||
5) Add helicopters with "Transport" main task for transport helicopters.
|
||||
6) Add planes with "CAS" main task for attack planes.
|
||||
7) Add planes with "CAP" main task for fighters.
|
||||
8) Configure loadouts, liveries, and skill for aircraft.
|
||||
|
||||
Tips:
|
||||
- The mission generator will only extract blue ground units from the template when selected from the "Blue Forces" menu, and vice versa.
|
||||
- Only unit types are used from ground units. Liveries or other attributes are able to be copied.
|
||||
- For aircraft, group size is currently capped at 2 units per group to help prevent issues with parking. Only the first unit in the group is used as a source.
|
||||
BIN
templates/Imports/FOB_16_SPWN_WIDE.miz
Normal file
20
templates/Imports/How to use imports.txt
Normal file
@ -0,0 +1,20 @@
|
||||
## Imports
|
||||
|
||||
A breakthrough feature of mission design with RotorOps is the ability to import complex arrangements of statics and vehicles. This allows you to create reusable mission assets like bases, FARPs, objective sites, or just about anything you can imagine building in the mission editor. You can place these on any map, at your desired point, rotation and coalition!
|
||||
|
||||
A selection of FOBs, FARPs, and other objects are available in the Imports folder. Included are multiplayer FOBs with up to 16 helicopter spawns! A guide to the included templates is available here: [RotorOps IMPORT Assets](http://dcs-helicopters.com/wp-content/uploads/2022/03/RotorOps_IMPORT_TEMPLATES-1.pdf)
|
||||
|
||||
|
||||
To use an import template:
|
||||
1) Place a static mark flag in the scenario template mission.
|
||||
2) Change the group name to 'IMPORT-filename', where the filename is a .miz file in the Generator/Imports folder.
|
||||
3) Change the flag coalition to CJTF Blue/Red or UN Peacekeepers.
|
||||
4) Change the flag heading and position as desired.
|
||||
5) Change the flag UNIT NAME to something relevant (ie. 'North Base')
|
||||
6) For multiple imports of the same template, the import object GROUP NAME should end with '-01' etc
|
||||
|
||||
To create a new import template:
|
||||
1) Make an empty mission on Caucasus.
|
||||
2) Place units/objects on the map.
|
||||
3) Make one unit group name: 'ANCHOR' This will represent the point of insertion/rotation in the target mission.
|
||||
4) Save the template .miz file in Generator/Imports
|
||||