mirror of
https://github.com/spencershepard/RotorOps.git
synced 2025-11-10 15:45:30 +00:00
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:
parent
07c4afa947
commit
8cda007768
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,3 +1,10 @@
|
|||||||
|
|
||||||
.project
|
.project
|
||||||
sound/desktop.ini
|
sound/desktop.ini
|
||||||
|
|
||||||
|
Generator/.idea
|
||||||
|
Generator/__pycache__
|
||||||
|
Generator/build
|
||||||
|
Generator/venv
|
||||||
|
Generator/Output
|
||||||
|
Generator/dist
|
||||||
9
Generator/Forces/_How to add your own templates.txt
Normal file
9
Generator/Forces/_How to add your own templates.txt
Normal 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.
|
||||||
BIN
Generator/Forces/blue/BLUE Default Armor.miz
Normal file
BIN
Generator/Forces/blue/BLUE Default Armor.miz
Normal file
Binary file not shown.
BIN
Generator/Forces/red/RED Default Mixed.miz
Normal file
BIN
Generator/Forces/red/RED Default Mixed.miz
Normal file
Binary file not shown.
206
Generator/MissionGenerator.py
Normal file
206
Generator/MissionGenerator.py
Normal 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())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
41
Generator/MissionGenerator.spec
Normal file
41
Generator/MissionGenerator.spec
Normal 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 )
|
||||||
365
Generator/MissionGeneratorUI.py
Normal file
365
Generator/MissionGeneratorUI.py
Normal 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_())
|
||||||
846
Generator/MissionGeneratorUI.ui
Normal file
846
Generator/MissionGeneratorUI.ui
Normal 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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
|
p, li { white-space: pre-wrap; }
|
||||||
|
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||||
|
<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></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>
|
||||||
BIN
Generator/Output/ROps Caucasus Nalchik to Beslan.miz
Normal file
BIN
Generator/Output/ROps Caucasus Nalchik to Beslan.miz
Normal file
Binary file not shown.
BIN
Generator/Output/ROps Conflict_Cauc_Batumi_Kobuleti.miz
Normal file
BIN
Generator/Output/ROps Conflict_Cauc_Batumi_Kobuleti.miz
Normal file
Binary file not shown.
BIN
Generator/Output/ROps Conflict_Cauc_Nalchik_Beslan.miz
Normal file
BIN
Generator/Output/ROps Conflict_Cauc_Nalchik_Beslan.miz
Normal file
Binary file not shown.
BIN
Generator/Output/ROps Nevada Conflict - Vegas Tour.miz
Normal file
BIN
Generator/Output/ROps Nevada Conflict - Vegas Tour.miz
Normal file
Binary file not shown.
BIN
Generator/Output/RotorOpsGenerateMission.miz
Normal file
BIN
Generator/Output/RotorOpsGenerateMission.miz
Normal file
Binary file not shown.
BIN
Generator/Output/g_Conflict_Cauc_Batumi_Kobuleti.miz
Normal file
BIN
Generator/Output/g_Conflict_Cauc_Batumi_Kobuleti.miz
Normal file
Binary file not shown.
BIN
Generator/Output/g_Conflict_Cauc_Nalchik_Beslan.miz
Normal file
BIN
Generator/Output/g_Conflict_Cauc_Nalchik_Beslan.miz
Normal file
Binary file not shown.
406
Generator/RotorOpsMission.py
Normal file
406
Generator/RotorOpsMission.py
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
26
Generator/RotorOpsUnits.py
Normal file
26
Generator/RotorOpsUnits.py
Normal 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"},
|
||||||
|
|
||||||
|
]
|
||||||
17
Generator/RotorOpsUtils.py
Normal file
17
Generator/RotorOpsUtils.py
Normal 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
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Generator/Scenarios/Nevada Conflict - Vegas Tour (GRIMM).miz
Normal file
BIN
Generator/Scenarios/Nevada Conflict - Vegas Tour (GRIMM).miz
Normal file
Binary file not shown.
BIN
Generator/Scenarios/PG Conflict - Abu Dhabi to Ras (GRIMM).miz
Normal file
BIN
Generator/Scenarios/PG Conflict - Abu Dhabi to Ras (GRIMM).miz
Normal file
Binary file not shown.
BIN
Generator/Scenarios/Syria Conflict - Aleppo Tour (GRIMM).miz
Normal file
BIN
Generator/Scenarios/Syria Conflict - Aleppo Tour (GRIMM).miz
Normal file
Binary file not shown.
20
Generator/Scenarios/_How to create your own scenarios.txt
Normal file
20
Generator/Scenarios/_How to create your own scenarios.txt
Normal 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.
|
||||||
BIN
Generator/Scenarios/pg_test.miz
Normal file
BIN
Generator/Scenarios/pg_test.miz
Normal file
Binary file not shown.
BIN
Generator/Scenarios/zone test.miz
Normal file
BIN
Generator/Scenarios/zone test.miz
Normal file
Binary file not shown.
BIN
Generator/assets/background.PNG
Normal file
BIN
Generator/assets/background.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
Generator/assets/icon.ico
Normal file
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
BIN
Generator/dist/MissionGenerator.exe
vendored
Normal file
Binary file not shown.
5
Generator/utils/embed sounds/How To Embed Sounds.txt
Normal file
5
Generator/utils/embed sounds/How To Embed Sounds.txt
Normal 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.
|
||||||
37
Generator/utils/embed sounds/embed_sounds.py
Normal file
37
Generator/utils/embed sounds/embed_sounds.py
Normal 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.')
|
||||||
40
Generator/utils/embed sounds/embed_sounds.spec
Normal file
40
Generator/utils/embed sounds/embed_sounds.spec
Normal 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 )
|
||||||
BIN
Mission Templates/RotorOps_template_Caucasus.miz
Normal file
BIN
Mission Templates/RotorOps_template_Caucasus.miz
Normal file
Binary file not shown.
BIN
Mission Templates/RotorOps_template_Mariana.miz
Normal file
BIN
Mission Templates/RotorOps_template_Mariana.miz
Normal file
Binary file not shown.
BIN
Mission Templates/RotorOps_template_Nevada.miz
Normal file
BIN
Mission Templates/RotorOps_template_Nevada.miz
Normal file
Binary file not shown.
BIN
Mission Templates/RotorOps_template_Normandy.miz
Normal file
BIN
Mission Templates/RotorOps_template_Normandy.miz
Normal file
Binary file not shown.
BIN
Mission Templates/RotorOps_template_Persian_Gulf.miz
Normal file
BIN
Mission Templates/RotorOps_template_Persian_Gulf.miz
Normal file
Binary file not shown.
BIN
Mission Templates/RotorOps_template_Syria.miz
Normal file
BIN
Mission Templates/RotorOps_template_Syria.miz
Normal file
Binary file not shown.
BIN
Mission Templates/RotorOps_template_The_Channel.miz
Normal file
BIN
Mission Templates/RotorOps_template_The_Channel.miz
Normal file
Binary file not shown.
BIN
MissionGenerator.exe
Normal file
BIN
MissionGenerator.exe
Normal file
Binary file not shown.
47
README.md
Normal file
47
README.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# What is RotorOps?
|
||||||
|
RotorOps brings the ground war in DCS to life. Infantry becomes useful, and the helicopter operations that support them will directly contribute to the success of the mission.
|
||||||
|
RotorOps is a DCS script that makes it easy to create fun and engaging missions on the fly, directly in the mission editor and without ever opening a script file.
|
||||||
|
|
||||||
|
## Demo Missions
|
||||||
|
RotorOps: Aleppo Under Siege https://www.digitalcombatsimulator.com/en/files/3320079/
|
||||||
|
|
||||||
|
# RotorOps: Conflict
|
||||||
|
At the heart of this first release is a game type called Conflict, where attacking forces must clear Conflict Zones of defending ground forces. Once a zone is cleared, the next zone is activated and ground vehicles will move to the next Conflict Zone automatically. It's up to the rotorheads to pickup troops from the cleared zones and transport them to the active Conflict Zone.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Do I have to transport troops?
|
||||||
|
This is really up to the mission designer. Transporting troops is not required for mission success in Conflict. However, friendly troops can be a very valuable asset, especially for clearing enemies in dense urban areas. If you're in a fixed wing or attack helicopter role, troop transport could be provided by other players or AI.
|
||||||
|
|
||||||
|
### What about attack helicopters?
|
||||||
|
The constantly moving infantry is easier to see than the statues we are used to seeing. Destroying defending enemy vehicles so that our troops and vehicles can survive, and intercepting enemy reinforcements may be crucial to mission success.
|
||||||
|
|
||||||
|
### How do I create a Conflict mission?
|
||||||
|
Just open a demo mission in the DCS mission editor and drop units into the Conflict Zones. These are trigger areas drawn in the mission editor that will automatically control the ground forces that enter them. This means that you do not need to worry about creating waypoints; enemy vehicles and infantry will seek each other out automatically. Move the Conflict Zones or change their size, add friendly or enemy units (remember, no waypoints needed).
|
||||||
|
|
||||||
|
Optional USER FLAGS are available to trigger events based on the status of individual zones and the game as a whole. Simple DO SCRIPT waypoint actions are available to drop troops from friendly or enemy AI helicopters or ground vehicles.
|
||||||
|
|
||||||
|
## RotorOps Mission Creator Guide: https://github.com/spencershepard/RotorOps/wiki/RotorOps:-Mission-Creator-Guide
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Get in touch!
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
https://discord.gg/HFqjrZV9xD
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Developers
|
||||||
|
We welcome contributors to this new project! Please get in touch on Discord with new ideas or pickup/create an issue in this repo.
|
||||||
|
|
||||||
|
|
||||||
|
***
|
||||||
|
RotorOps uses MIST and integrates CTLD:
|
||||||
|
|
||||||
|
https://github.com/mrSkortch/MissionScriptingTools
|
||||||
|
|
||||||
|
https://github.com/ciribob/DCS-CTLD
|
||||||
302
RotorOps.lua
302
RotorOps.lua
@ -1,5 +1,6 @@
|
|||||||
RotorOps = {}
|
RotorOps = {}
|
||||||
RotorOps.version = "1.2.1"
|
RotorOps.version = "1.2.3"
|
||||||
|
local debug = true
|
||||||
|
|
||||||
|
|
||||||
---[[ROTOROPS OPTIONS]]---
|
---[[ROTOROPS OPTIONS]]---
|
||||||
@ -8,15 +9,21 @@ RotorOps.version = "1.2.1"
|
|||||||
|
|
||||||
--RotorOps settings that are safe to change dynamically (ideally from the mission editor in DO SCRIPT for portability). You can change these while the script is running, at any time.
|
--RotorOps settings that are safe to change dynamically (ideally from the mission editor in DO SCRIPT for portability). You can change these while the script is running, at any time.
|
||||||
RotorOps.voice_overs = true
|
RotorOps.voice_overs = true
|
||||||
RotorOps.ground_speed = 60 --max speed for ground vehicles moving between zones
|
RotorOps.ground_speed = 60 --max speed for ground vehicles moving between zones. Doesn't have much effect since always limited by slowest vehicle in group
|
||||||
RotorOps.zone_status_display = true --constantly show units remaining and zone status on screen
|
RotorOps.zone_status_display = true --constantly show units remaining and zone status on screen
|
||||||
RotorOps.max_units_left = 0 --allow clearing the zone when a few units are left to prevent frustration with units getting stuck in buildings etc
|
RotorOps.max_units_left = 0 --allow clearing the zone when a few units are left to prevent frustration with units getting stuck in buildings etc
|
||||||
RotorOps.force_offroad = false --affects "move_to_zone" tasks only
|
RotorOps.force_offroad = false --affects "move_to_zone" tasks only
|
||||||
RotorOps.apcs_spawn_infantry = false --apcs will unload troops when arriving to a new zone
|
RotorOps.apcs_spawn_infantry = false --apcs will unload troops when arriving to a new zone
|
||||||
|
RotorOps.auto_push = true --should attacking ground units move to the next zone after clearing?
|
||||||
|
|
||||||
|
RotorOps.inf_spawns_avail = 0 --this is the number of infantry group spawn events remaining in the active zone
|
||||||
|
RotorOps.inf_spawn_chance = 25 -- 0-100 the chance of spawning infantry in an active zone spawn zone, per 'assessUnitsInZone' loop (10 seconds)
|
||||||
|
RotorOps.inf_spawn_trigger_percent = 70 --infantry has a chance of spawning if the percentage of defenders remaining in zone is less than this value
|
||||||
|
RotorOps.inf_spawns_per_zone = 3 --number of infantry groups to spawn per zone
|
||||||
|
|
||||||
|
|
||||||
--RotorOps settings that are safe to change only before calling setupConflict()
|
--RotorOps settings that are safe to change only before calling setupConflict()
|
||||||
RotorOps.transports = {'UH-1H', 'Mi-8MT', 'Mi-24P', 'SA342M', 'SA342L', 'SA342Mistral'} --players flying these will have ctld transport access
|
RotorOps.transports = {'UH-1H', 'Mi-8MT', 'Mi-24P', 'SA342M', 'SA342L', 'SA342Mistral'} --players flying these will have ctld transport access
|
||||||
RotorOps.auto_push = true --should attacking ground units move to the next zone after clearing?
|
|
||||||
RotorOps.CTLD_crates = false
|
RotorOps.CTLD_crates = false
|
||||||
RotorOps.CTLD_sound_effects = true --sound effects for troop pickup/dropoffs
|
RotorOps.CTLD_sound_effects = true --sound effects for troop pickup/dropoffs
|
||||||
RotorOps.exclude_ai_group_name = "noai" --include this somewhere in a group name to exclude the group from being tasked in the active zone
|
RotorOps.exclude_ai_group_name = "noai" --include this somewhere in a group name to exclude the group from being tasked in the active zone
|
||||||
@ -53,6 +60,7 @@ local game_message_buffer = {}
|
|||||||
local active_zone_initial_defenders
|
local active_zone_initial_defenders
|
||||||
local apcs = {} --table to keep track of infantry vehicles
|
local apcs = {} --table to keep track of infantry vehicles
|
||||||
local low_units_message_fired = false
|
local low_units_message_fired = false
|
||||||
|
local inf_spawn_zones = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -177,8 +185,10 @@ end
|
|||||||
---UTILITY FUNCTIONS---
|
---UTILITY FUNCTIONS---
|
||||||
|
|
||||||
local function debugMsg(text)
|
local function debugMsg(text)
|
||||||
trigger.action.outText(text, 5)
|
if(debug) then
|
||||||
env.info("ROTOROPS_DEBUG: "..text)
|
--trigger.action.outText(text, 5)
|
||||||
|
env.info("ROTOROPS_DEBUG: "..text)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -347,6 +357,45 @@ function RotorOps.spawnGroupOnGroup(grp, src_grp_name, ai_task) --allow to spawn
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--Spawn infantry in a trigger zone. Uses CTLD but may use another method in the future. Side is "red" or "blue"
|
||||||
|
--function RotorOps.spawnInfantryInZone(vars)
|
||||||
|
-- --local group = {mg=1,at=0,aa=0,inf=4,mortar=0}
|
||||||
|
--
|
||||||
|
-- local _triggerName = vars.zone
|
||||||
|
-- local _groupSide = vars.side
|
||||||
|
-- local _number = vars.qty
|
||||||
|
-- local _searchRadius = 500
|
||||||
|
--
|
||||||
|
-- local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position
|
||||||
|
--
|
||||||
|
-- if _spawnTrigger == nil then
|
||||||
|
-- env.warning("ERROR: Cant find zone called " .. _triggerName)
|
||||||
|
-- return
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- local _country
|
||||||
|
-- if _groupSide == "red" then
|
||||||
|
-- _groupSide = 1
|
||||||
|
-- _country = 0
|
||||||
|
-- else
|
||||||
|
-- _groupSide = 2
|
||||||
|
-- _country = 2
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- if _searchRadius < 0 then
|
||||||
|
-- _searchRadius = 0
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z }
|
||||||
|
-- local _alt = land.getHeight(_pos2)
|
||||||
|
-- local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y }
|
||||||
|
--
|
||||||
|
-- local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country)
|
||||||
|
--
|
||||||
|
-- local _droppedTroops = ctld.spawnDroppedGroup(_pos3, _groupDetails, false, _searchRadius);
|
||||||
|
-- --debugMsg(_groupDetails.groupName)
|
||||||
|
-- return _groupDetails.groupName --_ { units = _troops, groupId = _groupId, groupName = string.format("%s %i", _groupName, _groupId), side = _side, country = _country, weight = _weight, jtac = _hasJTAC }
|
||||||
|
--end
|
||||||
|
|
||||||
--Easy way to deploy troops from a vehicle with waypoint action. Spawns from the first valid unit found in a group
|
--Easy way to deploy troops from a vehicle with waypoint action. Spawns from the first valid unit found in a group
|
||||||
function RotorOps.deployTroops(quantity, target_group, announce)
|
function RotorOps.deployTroops(quantity, target_group, announce)
|
||||||
@ -410,6 +459,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
---AI CORE BEHAVIOR--
|
---AI CORE BEHAVIOR--
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
function RotorOps.chargeEnemy(vars)
|
function RotorOps.chargeEnemy(vars)
|
||||||
@ -427,61 +477,129 @@ function RotorOps.chargeEnemy(vars)
|
|||||||
if grp:getCoalition() == 1 then enemy_coal = 2 end
|
if grp:getCoalition() == 1 then enemy_coal = 2 end
|
||||||
if grp:getCoalition() == 2 then enemy_coal = 1 end
|
if grp:getCoalition() == 2 then enemy_coal = 1 end
|
||||||
|
|
||||||
local volS
|
local ifFound = function(foundItem, val) ---dcs world.searchObjects method
|
||||||
if vars.zone then
|
local enemy_unit
|
||||||
--debugMsg("CHARGE ENEMY at zone: "..vars.zone)
|
local path = {}
|
||||||
local sphere = trigger.misc.getZone(vars.zone)
|
--trigger.action.outText("found item: "..foundItem:getTypeName(), 5)
|
||||||
volS = {
|
-- if foundItem:hasAttribute("Infantry") == true and foundItem:getCoalition() == enemy_coal then
|
||||||
id = world.VolumeType.SPHERE,
|
if foundItem:getCoalition() == enemy_coal and foundItem:isActive() then
|
||||||
params = {
|
enemy_unit = foundItem
|
||||||
point = sphere.point,
|
--debugMsg("found enemy! "..foundItem:getTypeName())
|
||||||
radius = sphere.radius
|
|
||||||
}
|
path[1] = mist.ground.buildWP(start_point, '', 5)
|
||||||
}
|
path[2] = mist.ground.buildWP(enemy_unit:getPoint(), '', 5)
|
||||||
else
|
mist.goRoute(grp, path)
|
||||||
--debugMsg("CHARGE ENEMY in radius: "..search_radius)
|
else
|
||||||
volS = {
|
|
||||||
|
--trigger.action.outText("object found is not enemy inf in "..search_radius, 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if vars.zone then ---mist getUnitsInZones method
|
||||||
|
local units_in_zone = mist.getUnitsInZones(mist.makeUnitTable({'[red][vehicle]'}), {vars.zone}, "spherical")
|
||||||
|
local closest_dist = 10000
|
||||||
|
local closest_unit
|
||||||
|
for index, unit in pairs(units_in_zone) do
|
||||||
|
if unit:getCoalition() == enemy_coal then
|
||||||
|
local dist = mist.utils.get2DDist(start_point, unit:getPoint())
|
||||||
|
if dist < closest_dist then
|
||||||
|
closest_unit = unit
|
||||||
|
closest_dist = dist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if closest_unit ~= nil then
|
||||||
|
local path = {}
|
||||||
|
path[1] = mist.ground.buildWP(start_point, '', 5)
|
||||||
|
path[2] = mist.ground.buildWP(closest_unit:getPoint(), '', 5)
|
||||||
|
mist.goRoute(grp, path)
|
||||||
|
end
|
||||||
|
|
||||||
|
else ---dcs world.searchObjects method
|
||||||
|
--debugMsg("CHARGE ENEMY in radius: "..search_radius)
|
||||||
|
local volS = {
|
||||||
id = world.VolumeType.SPHERE,
|
id = world.VolumeType.SPHERE,
|
||||||
params = {
|
params = {
|
||||||
point = first_valid_unit:getPoint(),
|
point = first_valid_unit:getPoint(),
|
||||||
radius = search_radius
|
radius = search_radius
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local enemy_unit
|
|
||||||
local path = {}
|
|
||||||
local ifFound = function(foundItem, val)
|
|
||||||
--trigger.action.outText("found item: "..foundItem:getTypeName(), 5)
|
|
||||||
-- if foundItem:hasAttribute("Infantry") == true and foundItem:getCoalition() == enemy_coal then
|
|
||||||
if foundItem:getCoalition() == enemy_coal and foundItem:isActive() then
|
|
||||||
enemy_unit = foundItem
|
|
||||||
--debugMsg("found enemy! "..foundItem:getTypeName())
|
|
||||||
|
|
||||||
path[1] = mist.ground.buildWP(start_point, '', 5)
|
|
||||||
path[2] = mist.ground.buildWP(enemy_unit:getPoint(), '', 5)
|
|
||||||
--path[3] = mist.ground.buildWP(vars.spawn_point, '', 5)
|
|
||||||
else
|
|
||||||
|
|
||||||
--trigger.action.outText("object found is not enemy inf in "..search_radius, 5)
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
--default path if no units found
|
|
||||||
if false then
|
|
||||||
--debugMsg("group going back to origin")
|
|
||||||
path[1] = mist.ground.buildWP(start_point, '', 5)
|
|
||||||
path[2] = mist.ground.buildWP(vars.spawn_point, '', 5)
|
|
||||||
|
|
||||||
end
|
|
||||||
world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
|
||||||
mist.goRoute(grp, path)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--function RotorOps.chargeEnemy(vars)
|
||||||
|
-- --trigger.action.outText("charge enemies: "..mist.utils.tableShow(vars), 5)
|
||||||
|
-- local grp = vars.grp
|
||||||
|
-- local search_radius = vars.radius or 5000
|
||||||
|
-- ----
|
||||||
|
-- local first_valid_unit = RotorOps.getValidUnitFromGroup(grp)
|
||||||
|
--
|
||||||
|
-- if first_valid_unit == nil then return end
|
||||||
|
-- local start_point = first_valid_unit:getPoint()
|
||||||
|
-- if not vars.spawn_point then vars.spawn_point = start_point end
|
||||||
|
--
|
||||||
|
-- local enemy_coal
|
||||||
|
-- if grp:getCoalition() == 1 then enemy_coal = 2 end
|
||||||
|
-- if grp:getCoalition() == 2 then enemy_coal = 1 end
|
||||||
|
--
|
||||||
|
-- local volS
|
||||||
|
-- if vars.zone then
|
||||||
|
-- --debugMsg("CHARGE ENEMY at zone: "..vars.zone)
|
||||||
|
-- local sphere = trigger.misc.getZone(vars.zone)
|
||||||
|
-- volS = {
|
||||||
|
-- id = world.VolumeType.SPHERE,
|
||||||
|
-- params = {
|
||||||
|
-- point = sphere.point,
|
||||||
|
-- radius = sphere.radius
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
-- else
|
||||||
|
-- --debugMsg("CHARGE ENEMY in radius: "..search_radius)
|
||||||
|
-- volS = {
|
||||||
|
-- id = world.VolumeType.SPHERE,
|
||||||
|
-- params = {
|
||||||
|
-- point = first_valid_unit:getPoint(),
|
||||||
|
-- radius = search_radius
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- local enemy_unit
|
||||||
|
-- local path = {}
|
||||||
|
-- local ifFound = function(foundItem, val)
|
||||||
|
-- --trigger.action.outText("found item: "..foundItem:getTypeName(), 5)
|
||||||
|
-- -- if foundItem:hasAttribute("Infantry") == true and foundItem:getCoalition() == enemy_coal then
|
||||||
|
-- if foundItem:getCoalition() == enemy_coal and foundItem:isActive() then
|
||||||
|
-- enemy_unit = foundItem
|
||||||
|
-- --debugMsg("found enemy! "..foundItem:getTypeName())
|
||||||
|
--
|
||||||
|
-- path[1] = mist.ground.buildWP(start_point, '', 5)
|
||||||
|
-- path[2] = mist.ground.buildWP(enemy_unit:getPoint(), '', 5)
|
||||||
|
-- --path[3] = mist.ground.buildWP(vars.spawn_point, '', 5)
|
||||||
|
-- mist.goRoute(grp, path)
|
||||||
|
-- else
|
||||||
|
--
|
||||||
|
-- --trigger.action.outText("object found is not enemy inf in "..search_radius, 5)
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- return true
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--end
|
||||||
|
|
||||||
|
|
||||||
function RotorOps.patrolRadius(vars)
|
function RotorOps.patrolRadius(vars)
|
||||||
--debugMsg("patrol radius: "..mist.utils.tableShow(vars.grp))
|
--debugMsg("patrol radius: "..mist.utils.tableShow(vars.grp))
|
||||||
local grp = vars.grp
|
local grp = vars.grp
|
||||||
@ -551,21 +669,46 @@ end
|
|||||||
function RotorOps.aiExecute(vars)
|
function RotorOps.aiExecute(vars)
|
||||||
local update_interval = 60
|
local update_interval = 60
|
||||||
local last_task = vars.last_task
|
local last_task = vars.last_task
|
||||||
|
local last_zone = vars.last_zone
|
||||||
local group_name = vars.group_name
|
local group_name = vars.group_name
|
||||||
local task = RotorOps.ai_tasks[group_name].ai_task
|
local task = RotorOps.ai_tasks[group_name].ai_task
|
||||||
local zone = RotorOps.ai_tasks[group_name].zone
|
local zone = RotorOps.ai_tasks[group_name].zone
|
||||||
|
|
||||||
-- if vars.zone then zone = vars.zone end
|
-- if vars.zone then zone = vars.zone end
|
||||||
--debugMsg("tasking: "..group_name.." : "..task .." zone:"..zone)
|
|
||||||
|
|
||||||
if Group.isExist(Group.getByName(group_name)) ~= true or #Group.getByName(group_name):getUnits() < 1 then
|
if Group.isExist(Group.getByName(group_name)) ~= true or #Group.getByName(group_name):getUnits() < 1 then
|
||||||
--debugMsg("group no longer exists")
|
debugMsg(group_name.." no longer exists")
|
||||||
RotorOps.ai_tasks[group_name] = nil
|
RotorOps.ai_tasks[group_name] = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
--if Group.getByName(group_name):getController():hasTask() == false then --our implementation of hasTask does not seem to be working for vehicles
|
local same_zone = false
|
||||||
|
if zone ~= nil then
|
||||||
|
if zone ~= last_zone then
|
||||||
|
same_zone = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local should_update = true
|
||||||
|
|
||||||
|
-- if task == last_task then
|
||||||
|
-- should_update = false
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- if same_zone then
|
||||||
|
-- should_update = false
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- if task == "patrol" then
|
||||||
|
-- should_update = true
|
||||||
|
-- end
|
||||||
|
|
||||||
|
|
||||||
|
if should_update then --check to make sure we don't have the same task
|
||||||
|
|
||||||
|
debugMsg("tasking: "..group_name.." : "..task)
|
||||||
|
|
||||||
if task == "patrol" then
|
if task == "patrol" then
|
||||||
local vars = {}
|
local vars = {}
|
||||||
vars.grp = Group.getByName(group_name)
|
vars.grp = Group.getByName(group_name)
|
||||||
@ -599,8 +742,11 @@ function RotorOps.aiExecute(vars)
|
|||||||
local force_offroad = RotorOps.force_offroad
|
local force_offroad = RotorOps.force_offroad
|
||||||
mist.groupToPoint(group_name, RotorOps.active_zone, formation, final_heading, speed, force_offroad)
|
mist.groupToPoint(group_name, RotorOps.active_zone, formation, final_heading, speed, force_offroad)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
vars.last_task = task
|
vars.last_task = task
|
||||||
|
vars.last_zone = zone
|
||||||
|
|
||||||
local timer_id = timer.scheduleFunction(RotorOps.aiExecute, vars, timer.getTime() + update_interval)
|
local timer_id = timer.scheduleFunction(RotorOps.aiExecute, vars, timer.getTime() + update_interval)
|
||||||
end
|
end
|
||||||
@ -674,7 +820,18 @@ function RotorOps.assessUnitsInZone(var)
|
|||||||
--debugMsg("taking stock of the active zone")
|
--debugMsg("taking stock of the active zone")
|
||||||
active_zone_initial_defenders = defending_ground_units
|
active_zone_initial_defenders = defending_ground_units
|
||||||
low_units_message_fired = false
|
low_units_message_fired = false
|
||||||
env.info("ROTOR OPS: zone activated: "..RotorOps.active_zone)
|
|
||||||
|
--sort infantry spawn zones and spawn quantity
|
||||||
|
inf_spawn_zones = {}
|
||||||
|
for zone, zoneobj in pairs(mist.DBs.zonesByName) do
|
||||||
|
if string.find(zone, RotorOps.active_zone) and string.find(zone:lower(), "spawn") then --if we find a zone that has the active zone name and the word spawn
|
||||||
|
inf_spawn_zones[#inf_spawn_zones + 1] = zone
|
||||||
|
env.info("ROTOR OPS: spawn zone found:"..zone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RotorOps.inf_spawns_avail = RotorOps.inf_spawns_per_zone * #inf_spawn_zones
|
||||||
|
|
||||||
|
env.info("ROTOR OPS: zone activated: "..RotorOps.active_zone..", inf spawns avail:"..RotorOps.inf_spawns_avail..", spawn zones:"..#inf_spawn_zones)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -766,6 +923,7 @@ function RotorOps.assessUnitsInZone(var)
|
|||||||
if should_deploy then
|
if should_deploy then
|
||||||
local function timedDeploy()
|
local function timedDeploy()
|
||||||
if vehicle:isExist() then
|
if vehicle:isExist() then
|
||||||
|
env.info(vehicle:getName().." is deploying troops.")
|
||||||
RotorOps.deployTroops(4, vehicle:getGroup(), false)
|
RotorOps.deployTroops(4, vehicle:getGroup(), false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -781,6 +939,26 @@ function RotorOps.assessUnitsInZone(var)
|
|||||||
unloadAPCs() --this should really be an aitask
|
unloadAPCs() --this should really be an aitask
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--spawn infantry in infantry spawn zones
|
||||||
|
local function spawnInfantry()
|
||||||
|
if math.random(0, 100) <= RotorOps.inf_spawn_chance then
|
||||||
|
local rand_index = math.random(1, #inf_spawn_zones)
|
||||||
|
local zone = inf_spawn_zones[rand_index]
|
||||||
|
|
||||||
|
if RotorOps.defending then
|
||||||
|
ctld.spawnGroupAtTrigger("blue", 5, zone, 1000)
|
||||||
|
else
|
||||||
|
ctld.spawnGroupAtTrigger("red", 5, zone, 1000)
|
||||||
|
end
|
||||||
|
|
||||||
|
RotorOps.inf_spawns_avail = RotorOps.inf_spawns_avail - 1
|
||||||
|
env.info("ROTOR OPS: Spawned infantry. "..RotorOps.inf_spawns_avail.." spawns remaining in "..zone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if RotorOps.inf_spawns_avail > 0 and defenders_remaining_percent <= RotorOps.inf_spawn_trigger_percent then
|
||||||
|
spawnInfantry()
|
||||||
|
end
|
||||||
|
|
||||||
--voiceovers based on remaining defenders
|
--voiceovers based on remaining defenders
|
||||||
if not low_units_message_fired then
|
if not low_units_message_fired then
|
||||||
@ -818,6 +996,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function RotorOps.drawZones() --this could use a lot of work, we should use trigger.action.removeMark and some way of managing ids created
|
function RotorOps.drawZones() --this could use a lot of work, we should use trigger.action.removeMark and some way of managing ids created
|
||||||
local zones = RotorOps.zones
|
local zones = RotorOps.zones
|
||||||
local previous_point
|
local previous_point
|
||||||
@ -975,6 +1154,10 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function RotorOps.addZone(_name, _zone_defenders_flag)
|
function RotorOps.addZone(_name, _zone_defenders_flag)
|
||||||
|
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
|
||||||
table.insert(RotorOps.zones, {name = _name, defenders_status_flag = _zone_defenders_flag})
|
table.insert(RotorOps.zones, {name = _name, defenders_status_flag = _zone_defenders_flag})
|
||||||
trigger.action.setUserFlag(_zone_defenders_flag, 101)
|
trigger.action.setUserFlag(_zone_defenders_flag, 101)
|
||||||
RotorOps.drawZones()
|
RotorOps.drawZones()
|
||||||
@ -982,11 +1165,16 @@ function RotorOps.addZone(_name, _zone_defenders_flag)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function RotorOps.stagingZone(_name)
|
function RotorOps.stagingZone(_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, "blue", -1, "no", 0)
|
RotorOps.addPickupZone(_name, "blue", -1, "no", 0)
|
||||||
RotorOps.staging_zone = _name
|
RotorOps.staging_zone = _name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--function to automatically add transport craft to ctld, rather than having to define each in the mission editor
|
--function to automatically add transport craft to ctld, rather than having to define each in the mission editor
|
||||||
function RotorOps.addPilots(var)
|
function RotorOps.addPilots(var)
|
||||||
for uName, uData in pairs(mist.DBs.humansByName) do
|
for uName, uData in pairs(mist.DBs.humansByName) do
|
||||||
@ -1032,6 +1220,12 @@ function RotorOps.startConflict()
|
|||||||
|
|
||||||
RotorOps.staged_units = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), {RotorOps.staging_zone})
|
RotorOps.staged_units = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), {RotorOps.staging_zone})
|
||||||
|
|
||||||
|
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)
|
||||||
|
env.warning("No units in staging zone! Check RotorOps setup!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if RotorOps.staged_units[1]:getCoalition() == 1 then --check the coalition in the staging zone to see if we're defending
|
if RotorOps.staged_units[1]:getCoalition() == 1 then --check the coalition in the staging zone to see if we're defending
|
||||||
RotorOps.defending = true
|
RotorOps.defending = true
|
||||||
RotorOps.gameMsg(RotorOps.gameMsgs.start_defense)
|
RotorOps.gameMsg(RotorOps.gameMsgs.start_defense)
|
||||||
|
|||||||
@ -3,3 +3,4 @@ assert(loadfile("C:\\RotorOps\\mist_4_4_90.lua"))()
|
|||||||
assert(loadfile("C:\\RotorOps\\Splash_Damage_2_0.lua"))()
|
assert(loadfile("C:\\RotorOps\\Splash_Damage_2_0.lua"))()
|
||||||
assert(loadfile("C:\\RotorOps\\CTLD.lua"))()
|
assert(loadfile("C:\\RotorOps\\CTLD.lua"))()
|
||||||
assert(loadfile("C:\\RotorOps\\RotorOps.lua"))()
|
assert(loadfile("C:\\RotorOps\\RotorOps.lua"))()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user