Merge pull request #22 from spencershepard/feature/transport-helos

Feature/transport helos
This commit is contained in:
spencershepard 2022-02-19 12:46:04 -08:00 committed by GitHub
commit 58bdc17b00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1085 additions and 398 deletions

8
.gitignore vendored
View File

@ -6,4 +6,10 @@ Generator/.idea
Generator/__pycache__ Generator/__pycache__
Generator/build Generator/build
Generator/venv Generator/venv
Generator/dist Generator/dist
Generator/generator.log
MissionGenerator.exe
incoming templates/
Generator/utils/extract units/source.miz
Generator/utils/extract units/units.txt
generator.log

View File

@ -4,6 +4,15 @@ You can add your own unit templates in this directory and they will appear in th
2) Add ground unit groups. 2) Add ground unit groups.
3) Save the mission in this directory. 3) Save the mission in this directory.
Optional:
4) Add helicopters with "CAS" main task for attack helicopters.
5) Add helicopters with "Transport" main task for transport helicopters.
6) Add planes with "CAS" main task for attack planes.
7) Add planes with "CAP" main task for fighters.
8) Configure loadouts, liveries, and skill for aircraft.
Tips: Tips:
-Drop your templates in the RotorOps Discord if you'd like to have them added in a release for everyone. -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. -The mission generator will only extract blue ground units from the template when selected from the "Blue Forces" menu, and vice versa.
-Only unit types are used from ground units. Liveries or other attributes are able to be copied.
-For aircraft, group size is currently capped at 2 units per group to help prevent issues with parking. Only the first unit in the group is used as a source.

View File

@ -5,7 +5,7 @@ import dcs
import RotorOpsMission as ROps import RotorOpsMission as ROps
import RotorOpsUtils import RotorOpsUtils
import RotorOpsUnits import RotorOpsUnits
import logging
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QDialog, QMainWindow, QMessageBox QApplication, QDialog, QMainWindow, QMessageBox
@ -13,12 +13,38 @@ from PyQt5.QtWidgets import (
from PyQt5 import QtGui from PyQt5 import QtGui
from MissionGeneratorUI import Ui_MainWindow from MissionGeneratorUI import Ui_MainWindow
#Setup logfile and exception handler
logger = logging.getLogger(__name__)
logging.basicConfig(filename='generator.log', encoding='utf-8', level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)
def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt): #example of handling error subclasses
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
msg = QMessageBox()
msg.setWindowTitle("Uncaught exception")
msg.setText("Oops, there was a problem. Please check the log file or post it in the RotorOps discord where some helpful people will have a look.")
x = msg.exec_()
sys.excepthook = handle_exception
maj_version = 0 maj_version = 0
minor_version = 3 minor_version = 4
version_string = str(maj_version) + "." + str(minor_version) version_string = str(maj_version) + "." + str(minor_version)
scenarios = [] scenarios = []
red_forces_files = [] red_forces_files = []
blue_forces_files = [] blue_forces_files = []
defenders_text = "Defending Forces:"
attackers_text = "Attacking Forces:"
logger.info("RotorOps v" + version_string)
class Window(QMainWindow, Ui_MainWindow): class Window(QMainWindow, Ui_MainWindow):
@ -27,11 +53,11 @@ class Window(QMainWindow, Ui_MainWindow):
super().__init__(parent) super().__init__(parent)
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
print('running in a PyInstaller bundle') logger.info('running in a PyInstaller bundle')
home_dir = os.getcwd() home_dir = os.getcwd()
os.chdir(home_dir + "/Generator") os.chdir(home_dir + "/Generator")
else: else:
print('running in a normal Python process') logger.info('running in a normal Python process')
self.m = ROps.RotorOpsMission() self.m = ROps.RotorOpsMission()
@ -42,6 +68,8 @@ class Window(QMainWindow, Ui_MainWindow):
self.populateForces("blue", self.blueforces_comboBox, blue_forces_files) self.populateForces("blue", self.blueforces_comboBox, blue_forces_files)
self.populateSlotSelection() self.populateSlotSelection()
self.blue_forces_label.setText(attackers_text)
self.red_forces_label.setText(defenders_text)
self.background_label.setPixmap(QtGui.QPixmap(self.m.assets_dir + "/background.PNG")) self.background_label.setPixmap(QtGui.QPixmap(self.m.assets_dir + "/background.PNG"))
self.statusbar.setStyleSheet( self.statusbar.setStyleSheet(
"QStatusBar{padding-left:5px;color:black;font-weight:bold;}") "QStatusBar{padding-left:5px;color:black;font-weight:bold;}")
@ -50,15 +78,15 @@ class Window(QMainWindow, Ui_MainWindow):
def connectSignalsSlots(self): def connectSignalsSlots(self):
# self.action_Exit.triggered.connect(self.close)
self.action_generateMission.triggered.connect(self.generateMissionAction) self.action_generateMission.triggered.connect(self.generateMissionAction)
self.action_scenarioSelected.triggered.connect(self.scenarioChanged) self.action_scenarioSelected.triggered.connect(self.scenarioChanged)
self.action_defensiveModeChanged.triggered.connect(self.defensiveModeChanged)
def populateScenarios(self): def populateScenarios(self):
os.chdir(self.m.scenarios_dir) os.chdir(self.m.scenarios_dir)
path = os.getcwd() path = os.getcwd()
dir_list = os.listdir(path) dir_list = os.listdir(path)
print("Looking for mission files in '", path, "' :") logger.info("Looking for mission files in " + path)
for filename in dir_list: for filename in dir_list:
if filename.endswith(".miz"): if filename.endswith(".miz"):
@ -70,7 +98,7 @@ class Window(QMainWindow, Ui_MainWindow):
os.chdir(self.m.forces_dir + "/" + side) os.chdir(self.m.forces_dir + "/" + side)
path = os.getcwd() path = os.getcwd()
dir_list = os.listdir(path) dir_list = os.listdir(path)
print("Looking for " + side + " Forces files in '", os.getcwd(), "' :") logger.info("Looking for " + side + " Forces files in '" + path)
for filename in dir_list: for filename in dir_list:
if filename.endswith(".miz"): if filename.endswith(".miz"):
@ -82,65 +110,71 @@ class Window(QMainWindow, Ui_MainWindow):
for type in RotorOpsUnits.client_helos: for type in RotorOpsUnits.client_helos:
self.slot_template_comboBox.addItem(type.id) self.slot_template_comboBox.addItem(type.id)
def defensiveModeChanged(self):
if self.defense_checkBox.isChecked():
self.red_forces_label.setText(attackers_text)
self.blue_forces_label.setText(defenders_text)
else:
self.red_forces_label.setText(defenders_text)
self.blue_forces_label.setText(attackers_text)
def scenarioChanged(self): def scenarioChanged(self):
try: os.chdir(self.m.scenarios_dir)
os.chdir(self.m.scenarios_dir) filename = scenarios[self.scenario_comboBox.currentIndex()]
filename = scenarios[self.scenario_comboBox.currentIndex()] source_mission = dcs.mission.Mission()
source_mission = dcs.mission.Mission() source_mission.load_file(filename)
source_mission.load_file(filename) zones = source_mission.triggers.zones()
zones = source_mission.triggers.zones() conflict_zones = 0
conflict_zones = 0 staging_zones = 0
staging_zones = 0 conflict_zone_size_sum = 0
conflict_zone_size_sum = 0 conflict_zone_distance_sum = 0
conflict_zone_distance_sum = 0 spawn_zones = 0
spawn_zones = 0 conflict_zone_positions = []
conflict_zone_positions = [] #friendly_airports = source_mission.getCoalitionAirports("blue")
#friendly_airports = source_mission.getCoalitionAirports("blue") #enemy_airports = source_mission.getCoalitionAirports("red")
#enemy_airports = source_mission.getCoalitionAirports("red") friendly_airports = True
friendly_airports = True enemy_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])
for zone in zones: def validateTemplate():
if zone.name == "STAGING": valid = True
staging_zones += 1 if len(staging_zones) < 1:
if zone.name == "ALPHA" or zone.name == "BRAVO" or zone.name == "CHARLIE" or zone.name == "DELTA": valid = False
conflict_zones += 1 if len(conflict_zones) < 1:
conflict_zone_size_sum += zone.radius valid = False
conflict_zone_positions.append(zone.position) if not friendly_airports:
if zone.name.rfind("_SPAWN") > 0: valid = False
spawn_zones += 1 if not enemy_airports:
if conflict_zones > 1: valid = False
for index, position in enumerate(conflict_zone_positions): return valid
if index > 0:
conflict_zone_distance_sum += RotorOpsUtils.getDistance(conflict_zone_positions[index], conflict_zone_positions[index - 1])
def validateTemplate(): if conflict_zones and staging_zones :
valid = True average_zone_size = conflict_zone_size_sum / conflict_zones
if len(staging_zones) < 1: self.description_textBrowser.setText(
valid = False "Map: " + source_mission.terrain.name + "\n" +
if len(conflict_zones) < 1: "Conflict Zones: " + str(conflict_zones) + "\n" +
valid = False "Average Zone Size " + str(math.floor(average_zone_size)) + "m \n" +
if not friendly_airports: "Infantry Spawn Zones: " + str(spawn_zones) + "\n" +
valid = False "Approx Distance: " + str(math.floor(RotorOpsUtils.convertMeterToNM(conflict_zone_distance_sum))) + "nm \n"
if not enemy_airports: #"Validity Check:" + str(validateTemplate())
valid = False + "\n== BRIEFING ==\n\n"
return valid + source_mission.description_text()
)
if conflict_zones and staging_zones : #self.description_textBrowser.setText("File error occured.")
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): def generateMissionAction(self):
@ -168,19 +202,22 @@ class Window(QMainWindow, Ui_MainWindow):
"zone_protect_sams": self.zone_sams_checkBox.isChecked(), "zone_protect_sams": self.zone_sams_checkBox.isChecked(),
"zone_farps": self.farp_buttonGroup.checkedButton().objectName(), "zone_farps": self.farp_buttonGroup.checkedButton().objectName(),
"inf_spawn_msgs": self.inf_spawn_voiceovers_checkBox.isChecked(), "inf_spawn_msgs": self.inf_spawn_voiceovers_checkBox.isChecked(),
"e_transport_helos": self.e_transport_helos_spinBox.value(),
"transport_drop_qty": self.troop_drop_spinBox.value(),
"smoke_pickup_zones": self.smoke_pickup_zone_checkBox.isChecked(),
} }
os.chdir(self.m.home_dir + '/Generator') os.chdir(self.m.home_dir + '/Generator')
n = ROps.RotorOpsMission() n = ROps.RotorOpsMission()
result = n.generateMission(data) result = n.generateMission(data)
print("Generating mission with options:") logger.info("Generating mission with options:")
print(str(data)) logger.info(str(data))
# generate the mission # generate the mission
#result = self.m.generateMission(data) #result = self.m.generateMission(data)
#display results #display results
if result["success"]: if result["success"]:
print(result["filename"] + "' successfully generated in " + result["directory"]) logger.info(result["filename"] + "' successfully generated in " + result["directory"])
self.statusbar.showMessage(result["filename"] + "' successfully generated in " + result["directory"], 10000) self.statusbar.showMessage(result["filename"] + "' successfully generated in " + result["directory"], 10000)
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle("Mission Generated") msg.setWindowTitle("Mission Generated")
@ -199,7 +236,7 @@ class Window(QMainWindow, Ui_MainWindow):
) )
x = msg.exec_() x = msg.exec_()
elif not result["success"]: elif not result["success"]:
print(result["failure_msg"]) logger.warning(result["failure_msg"])
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle("Error") msg.setWindowTitle("Error")
msg.setText(result["failure_msg"]) msg.setText(result["failure_msg"])

View File

@ -48,7 +48,7 @@ class Ui_MainWindow(object):
"padding: 4px;") "padding: 4px;")
self.generateButton.setObjectName("generateButton") self.generateButton.setObjectName("generateButton")
self.description_textBrowser = QtWidgets.QTextBrowser(self.centralwidget) self.description_textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.description_textBrowser.setGeometry(QtCore.QRect(710, 20, 331, 131)) self.description_textBrowser.setGeometry(QtCore.QRect(670, 30, 501, 131))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(9) font.setPointSize(9)
self.description_textBrowser.setFont(font) self.description_textBrowser.setFont(font)
@ -57,23 +57,23 @@ class Ui_MainWindow(object):
self.blueforces_comboBox = QtWidgets.QComboBox(self.centralwidget) self.blueforces_comboBox = QtWidgets.QComboBox(self.centralwidget)
self.blueforces_comboBox.setGeometry(QtCore.QRect(790, 230, 291, 31)) self.blueforces_comboBox.setGeometry(QtCore.QRect(790, 230, 291, 31))
self.blueforces_comboBox.setObjectName("blueforces_comboBox") self.blueforces_comboBox.setObjectName("blueforces_comboBox")
self.scenario_label_2 = QtWidgets.QLabel(self.centralwidget) self.blue_forces_label = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_2.setGeometry(QtCore.QRect(690, 180, 141, 31)) self.blue_forces_label.setGeometry(QtCore.QRect(690, 180, 241, 31))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(12) font.setPointSize(12)
self.scenario_label_2.setFont(font) self.blue_forces_label.setFont(font)
self.scenario_label_2.setObjectName("scenario_label_2") self.blue_forces_label.setObjectName("blue_forces_label")
self.scenario_label_3 = QtWidgets.QLabel(self.centralwidget) self.red_forces_label = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_3.setGeometry(QtCore.QRect(60, 180, 141, 31)) self.red_forces_label.setGeometry(QtCore.QRect(60, 180, 261, 31))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(12) font.setPointSize(12)
self.scenario_label_3.setFont(font) self.red_forces_label.setFont(font)
self.scenario_label_3.setObjectName("scenario_label_3") self.red_forces_label.setObjectName("red_forces_label")
self.redforces_comboBox = QtWidgets.QComboBox(self.centralwidget) self.redforces_comboBox = QtWidgets.QComboBox(self.centralwidget)
self.redforces_comboBox.setGeometry(QtCore.QRect(170, 230, 291, 31)) self.redforces_comboBox.setGeometry(QtCore.QRect(170, 230, 291, 31))
self.redforces_comboBox.setObjectName("redforces_comboBox") self.redforces_comboBox.setObjectName("redforces_comboBox")
self.background_label = QtWidgets.QLabel(self.centralwidget) self.background_label = QtWidgets.QLabel(self.centralwidget)
self.background_label.setGeometry(QtCore.QRect(-40, 440, 801, 371)) self.background_label.setGeometry(QtCore.QRect(-40, 490, 801, 371))
self.background_label.setAutoFillBackground(False) self.background_label.setAutoFillBackground(False)
self.background_label.setStyleSheet("") self.background_label.setStyleSheet("")
self.background_label.setText("") self.background_label.setText("")
@ -113,7 +113,7 @@ class Ui_MainWindow(object):
self.scenario_label_4.setAlignment(QtCore.Qt.AlignCenter) self.scenario_label_4.setAlignment(QtCore.Qt.AlignCenter)
self.scenario_label_4.setObjectName("scenario_label_4") self.scenario_label_4.setObjectName("scenario_label_4")
self.game_status_checkBox = QtWidgets.QCheckBox(self.centralwidget) self.game_status_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.game_status_checkBox.setGeometry(QtCore.QRect(810, 790, 191, 16)) self.game_status_checkBox.setGeometry(QtCore.QRect(810, 760, 191, 16))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(9) font.setPointSize(9)
self.game_status_checkBox.setFont(font) self.game_status_checkBox.setFont(font)
@ -121,7 +121,7 @@ class Ui_MainWindow(object):
self.game_status_checkBox.setTristate(False) self.game_status_checkBox.setTristate(False)
self.game_status_checkBox.setObjectName("game_status_checkBox") self.game_status_checkBox.setObjectName("game_status_checkBox")
self.voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget) self.voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.voiceovers_checkBox.setGeometry(QtCore.QRect(810, 820, 191, 16)) self.voiceovers_checkBox.setGeometry(QtCore.QRect(810, 790, 191, 16))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(9) font.setPointSize(9)
self.voiceovers_checkBox.setFont(font) self.voiceovers_checkBox.setFont(font)
@ -150,13 +150,14 @@ class Ui_MainWindow(object):
self.tankers_checkBox.setChecked(True) self.tankers_checkBox.setChecked(True)
self.tankers_checkBox.setObjectName("tankers_checkBox") self.tankers_checkBox.setObjectName("tankers_checkBox")
self.apcs_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget) self.apcs_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(470, 400, 251, 31)) self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(450, 420, 251, 31))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(10) font.setPointSize(10)
self.apcs_spawn_checkBox.setFont(font) self.apcs_spawn_checkBox.setFont(font)
self.apcs_spawn_checkBox.setChecked(True)
self.apcs_spawn_checkBox.setObjectName("apcs_spawn_checkBox") self.apcs_spawn_checkBox.setObjectName("apcs_spawn_checkBox")
self.inf_spawn_spinBox = QtWidgets.QSpinBox(self.centralwidget) self.inf_spawn_spinBox = QtWidgets.QSpinBox(self.centralwidget)
self.inf_spawn_spinBox.setGeometry(QtCore.QRect(680, 360, 71, 31)) self.inf_spawn_spinBox.setGeometry(QtCore.QRect(670, 340, 51, 31))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(12) font.setPointSize(12)
self.inf_spawn_spinBox.setFont(font) self.inf_spawn_spinBox.setFont(font)
@ -176,7 +177,7 @@ class Ui_MainWindow(object):
self.forces_hint_label_2.setAlignment(QtCore.Qt.AlignCenter) self.forces_hint_label_2.setAlignment(QtCore.Qt.AlignCenter)
self.forces_hint_label_2.setObjectName("forces_hint_label_2") self.forces_hint_label_2.setObjectName("forces_hint_label_2")
self.label = QtWidgets.QLabel(self.centralwidget) self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(470, 360, 191, 31)) self.label.setGeometry(QtCore.QRect(450, 340, 211, 21))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(10) font.setPointSize(10)
self.label.setFont(font) self.label.setFont(font)
@ -190,14 +191,8 @@ class Ui_MainWindow(object):
font.setPointSize(11) font.setPointSize(11)
self.label_2.setFont(font) self.label_2.setFont(font)
self.label_2.setObjectName("label_2") 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 = QtWidgets.QCheckBox(self.centralwidget)
self.force_offroad_checkBox.setGeometry(QtCore.QRect(810, 760, 191, 16)) self.force_offroad_checkBox.setGeometry(QtCore.QRect(810, 820, 191, 16))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(9) font.setPointSize(9)
self.force_offroad_checkBox.setFont(font) self.force_offroad_checkBox.setFont(font)
@ -247,15 +242,15 @@ class Ui_MainWindow(object):
self.zone_sams_checkBox.setFont(font) self.zone_sams_checkBox.setFont(font)
self.zone_sams_checkBox.setObjectName("zone_sams_checkBox") self.zone_sams_checkBox.setObjectName("zone_sams_checkBox")
self.scenario_label_9 = QtWidgets.QLabel(self.centralwidget) self.scenario_label_9 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_9.setGeometry(QtCore.QRect(740, 490, 171, 31)) self.scenario_label_9.setGeometry(QtCore.QRect(810, 450, 171, 31))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(10) font.setPointSize(10)
self.scenario_label_9.setFont(font) self.scenario_label_9.setFont(font)
self.scenario_label_9.setObjectName("scenario_label_9") self.scenario_label_9.setObjectName("scenario_label_9")
self.inf_spawn_voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget) self.inf_spawn_voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.inf_spawn_voiceovers_checkBox.setGeometry(QtCore.QRect(470, 430, 251, 31)) self.inf_spawn_voiceovers_checkBox.setGeometry(QtCore.QRect(810, 720, 251, 31))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(10) font.setPointSize(9)
self.inf_spawn_voiceovers_checkBox.setFont(font) self.inf_spawn_voiceovers_checkBox.setFont(font)
self.inf_spawn_voiceovers_checkBox.setChecked(True) self.inf_spawn_voiceovers_checkBox.setChecked(True)
self.inf_spawn_voiceovers_checkBox.setObjectName("inf_spawn_voiceovers_checkBox") self.inf_spawn_voiceovers_checkBox.setObjectName("inf_spawn_voiceovers_checkBox")
@ -287,14 +282,51 @@ class Ui_MainWindow(object):
self.version_label.setGeometry(QtCore.QRect(920, 840, 241, 21)) self.version_label.setGeometry(QtCore.QRect(920, 840, 241, 21))
self.version_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.version_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.version_label.setObjectName("version_label") self.version_label.setObjectName("version_label")
self.scenario_label_10 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_10.setGeometry(QtCore.QRect(140, 410, 241, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.scenario_label_10.setFont(font)
self.scenario_label_10.setObjectName("scenario_label_10")
self.e_transport_helos_spinBox = QtWidgets.QSpinBox(self.centralwidget)
self.e_transport_helos_spinBox.setGeometry(QtCore.QRect(70, 410, 51, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.e_transport_helos_spinBox.setFont(font)
self.e_transport_helos_spinBox.setMinimum(0)
self.e_transport_helos_spinBox.setMaximum(8)
self.e_transport_helos_spinBox.setProperty("value", 1)
self.e_transport_helos_spinBox.setObjectName("e_transport_helos_spinBox")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(450, 380, 191, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.troop_drop_spinBox = QtWidgets.QSpinBox(self.centralwidget)
self.troop_drop_spinBox.setGeometry(QtCore.QRect(670, 380, 51, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.troop_drop_spinBox.setFont(font)
self.troop_drop_spinBox.setMinimum(0)
self.troop_drop_spinBox.setMaximum(10)
self.troop_drop_spinBox.setProperty("value", 4)
self.troop_drop_spinBox.setObjectName("troop_drop_spinBox")
self.smoke_pickup_zone_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.smoke_pickup_zone_checkBox.setGeometry(QtCore.QRect(810, 690, 251, 31))
font = QtGui.QFont()
font.setPointSize(9)
self.smoke_pickup_zone_checkBox.setFont(font)
self.smoke_pickup_zone_checkBox.setChecked(True)
self.smoke_pickup_zone_checkBox.setObjectName("smoke_pickup_zone_checkBox")
self.background_label.raise_() self.background_label.raise_()
self.scenario_comboBox.raise_() self.scenario_comboBox.raise_()
self.scenario_label.raise_() self.scenario_label.raise_()
self.generateButton.raise_() self.generateButton.raise_()
self.description_textBrowser.raise_() self.description_textBrowser.raise_()
self.blueforces_comboBox.raise_() self.blueforces_comboBox.raise_()
self.scenario_label_2.raise_() self.blue_forces_label.raise_()
self.scenario_label_3.raise_() self.red_forces_label.raise_()
self.redforces_comboBox.raise_() self.redforces_comboBox.raise_()
self.scenario_hint_label.raise_() self.scenario_hint_label.raise_()
self.forces_hint_label.raise_() self.forces_hint_label.raise_()
@ -313,7 +345,6 @@ class Ui_MainWindow(object):
self.label.raise_() self.label.raise_()
self.slot_template_comboBox.raise_() self.slot_template_comboBox.raise_()
self.label_2.raise_() self.label_2.raise_()
self.scenario_label_6.raise_()
self.force_offroad_checkBox.raise_() self.force_offroad_checkBox.raise_()
self.defense_checkBox.raise_() self.defense_checkBox.raise_()
self.e_attack_helos_spinBox.raise_() self.e_attack_helos_spinBox.raise_()
@ -327,6 +358,11 @@ class Ui_MainWindow(object):
self.farp_gunits.raise_() self.farp_gunits.raise_()
self.farp_always.raise_() self.farp_always.raise_()
self.version_label.raise_() self.version_label.raise_()
self.scenario_label_10.raise_()
self.e_transport_helos_spinBox.raise_()
self.label_3.raise_()
self.troop_drop_spinBox.raise_()
self.smoke_pickup_zone_checkBox.raise_()
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1209, 26)) self.menubar.setGeometry(QtCore.QRect(0, 0, 1209, 26))
@ -344,10 +380,13 @@ class Ui_MainWindow(object):
self.action_blueforcesSelected.setObjectName("action_blueforcesSelected") self.action_blueforcesSelected.setObjectName("action_blueforcesSelected")
self.action_redforcesSelected = QtWidgets.QAction(MainWindow) self.action_redforcesSelected = QtWidgets.QAction(MainWindow)
self.action_redforcesSelected.setObjectName("action_redforcesSelected") self.action_redforcesSelected.setObjectName("action_redforcesSelected")
self.action_defensiveModeChanged = QtWidgets.QAction(MainWindow)
self.action_defensiveModeChanged.setObjectName("action_defensiveModeChanged")
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
self.generateButton.clicked.connect(self.action_generateMission.trigger) self.generateButton.clicked.connect(self.action_generateMission.trigger)
self.scenario_comboBox.currentIndexChanged['int'].connect(self.action_scenarioSelected.trigger) self.scenario_comboBox.currentIndexChanged['int'].connect(self.action_scenarioSelected.trigger)
self.defense_checkBox.stateChanged['int'].connect(self.action_defensiveModeChanged.trigger)
QtCore.QMetaObject.connectSlotsByName(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow): def retranslateUi(self, MainWindow):
@ -357,13 +396,13 @@ class Ui_MainWindow(object):
self.scenario_label.setText(_translate("MainWindow", "Scenario Template:")) self.scenario_label.setText(_translate("MainWindow", "Scenario Template:"))
self.generateButton.setText(_translate("MainWindow", "Generate Mission")) 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" 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" "<html><head><meta name=\"qrichtext\" content=\"1\" /><meta charset=\"utf-8\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\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" "</style></head><body style=\" font-family:\'Segoe UI\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Provide close air support for our convoys as we take back Las Vegas from the enemy!</span></p></body></html>")) "<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:10pt;\">Provide close air support for our convoys as we take back Las Vegas from the enemy!</span></p></body></html>"))
self.blueforces_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own custom ground forces groups to be automatically generated.")) 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.blue_forces_label.setText(_translate("MainWindow", "Friendly Forces:"))
self.scenario_label_3.setText(_translate("MainWindow", "Enemy Forces:")) self.red_forces_label.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.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.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.forces_hint_label.setText(_translate("MainWindow", "Forces templates are .miz files in \'Generator/Forces\'"))
@ -378,20 +417,22 @@ class Ui_MainWindow(object):
self.logistics_crates_checkBox.setText(_translate("MainWindow", "Logistics")) self.logistics_crates_checkBox.setText(_translate("MainWindow", "Logistics"))
self.awacs_checkBox.setText(_translate("MainWindow", "Friendly AWACS")) self.awacs_checkBox.setText(_translate("MainWindow", "Friendly AWACS"))
self.tankers_checkBox.setText(_translate("MainWindow", "Friendly Tankers")) 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.setStatusTip(_translate("MainWindow", "Friendly/enemy APCs will drop infantry when reaching a new conflict zone. Disables infinite troop pickups from conflict zones (you must pick up existing troops)."))
self.apcs_spawn_checkBox.setText(_translate("MainWindow", "APCs Spawn Infantry")) self.apcs_spawn_checkBox.setText(_translate("MainWindow", "APCs Spawn Infantry"))
self.inf_spawn_spinBox.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template.")) self.inf_spawn_spinBox.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
self.scenario_label_5.setText(_translate("MainWindow", "Groups Per 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.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.label.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
self.label.setText(_translate("MainWindow", "Infantry Spawns per zone:"))
self.slot_template_comboBox.setStatusTip(_translate("MainWindow", "Default player/client spawn locations at a friendly airport.")) 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.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. "))
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.force_offroad_checkBox.setText(_translate("MainWindow", "Force Offroad"))
self.defense_checkBox.setText(_translate("MainWindow", "Defensive Mode")) self.defense_checkBox.setText(_translate("MainWindow", "Defensive Mode"))
self.e_attack_helos_spinBox.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack helicopter group spawns.")) self.e_attack_helos_spinBox.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack helicopter group spawns."))
self.scenario_label_7.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack helicopter group spawns."))
self.scenario_label_7.setText(_translate("MainWindow", "Enemy Attack Helicopters")) self.scenario_label_7.setText(_translate("MainWindow", "Enemy Attack Helicopters"))
self.scenario_label_8.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack plane group spawns."))
self.scenario_label_8.setText(_translate("MainWindow", "Enemy Attack Planes")) self.scenario_label_8.setText(_translate("MainWindow", "Enemy Attack Planes"))
self.e_attack_planes_spinBox.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack plane group spawns.")) self.e_attack_planes_spinBox.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack plane group spawns."))
self.zone_sams_checkBox.setStatusTip(_translate("MainWindow", "Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed.")) self.zone_sams_checkBox.setStatusTip(_translate("MainWindow", "Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed."))
@ -406,10 +447,19 @@ class Ui_MainWindow(object):
self.farp_always.setStatusTip(_translate("MainWindow", "Always spawn a FARP in defeated conflict zones.")) self.farp_always.setStatusTip(_translate("MainWindow", "Always spawn a FARP in defeated conflict zones."))
self.farp_always.setText(_translate("MainWindow", "Always")) self.farp_always.setText(_translate("MainWindow", "Always"))
self.version_label.setText(_translate("MainWindow", "Version string")) self.version_label.setText(_translate("MainWindow", "Version string"))
self.scenario_label_10.setStatusTip(_translate("MainWindow", "Approximate number of enemy transport helicopter spawns."))
self.scenario_label_10.setText(_translate("MainWindow", "Enemy Transport Helicopters"))
self.e_transport_helos_spinBox.setStatusTip(_translate("MainWindow", "Approximate number of enemy transport helicopter spawns."))
self.label_3.setStatusTip(_translate("MainWindow", "The number of troop drops per transport helicopter flight."))
self.label_3.setText(_translate("MainWindow", "Transport Drop Points:"))
self.troop_drop_spinBox.setStatusTip(_translate("MainWindow", "The number of troop drops per transport helicopter flight."))
self.smoke_pickup_zone_checkBox.setStatusTip(_translate("MainWindow", "Infinite troop pickup zones will be marked with blue smoke."))
self.smoke_pickup_zone_checkBox.setText(_translate("MainWindow", "Smoke at Troop Pickup Zones"))
self.action_generateMission.setText(_translate("MainWindow", "_generateMission")) self.action_generateMission.setText(_translate("MainWindow", "_generateMission"))
self.action_scenarioSelected.setText(_translate("MainWindow", "_scenarioSelected")) self.action_scenarioSelected.setText(_translate("MainWindow", "_scenarioSelected"))
self.action_blueforcesSelected.setText(_translate("MainWindow", "_blueforcesSelected")) self.action_blueforcesSelected.setText(_translate("MainWindow", "_blueforcesSelected"))
self.action_redforcesSelected.setText(_translate("MainWindow", "_redforcesSelected")) self.action_redforcesSelected.setText(_translate("MainWindow", "_redforcesSelected"))
self.action_defensiveModeChanged.setText(_translate("MainWindow", "_defensiveModeChanged"))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -96,9 +96,9 @@ padding: 4px;</string>
<widget class="QTextBrowser" name="description_textBrowser"> <widget class="QTextBrowser" name="description_textBrowser">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>710</x> <x>670</x>
<y>20</y> <y>30</y>
<width>331</width> <width>501</width>
<height>131</height> <height>131</height>
</rect> </rect>
</property> </property>
@ -112,10 +112,10 @@ padding: 4px;</string>
</property> </property>
<property name="html"> <property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt; <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt; &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; } p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt; &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Provide close air support for our convoys as we take back Las Vegas from the enemy!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> &lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Provide close air support for our convoys as we take back Las Vegas from the enemy!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
<widget class="QComboBox" name="blueforces_comboBox"> <widget class="QComboBox" name="blueforces_comboBox">
@ -131,12 +131,12 @@ p, li { white-space: pre-wrap; }
<string>Tip: You can create your own custom ground forces groups to be automatically generated.</string> <string>Tip: You can create your own custom ground forces groups to be automatically generated.</string>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="scenario_label_2"> <widget class="QLabel" name="blue_forces_label">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>690</x> <x>690</x>
<y>180</y> <y>180</y>
<width>141</width> <width>241</width>
<height>31</height> <height>31</height>
</rect> </rect>
</property> </property>
@ -149,12 +149,12 @@ p, li { white-space: pre-wrap; }
<string>Friendly Forces:</string> <string>Friendly Forces:</string>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="scenario_label_3"> <widget class="QLabel" name="red_forces_label">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>60</x> <x>60</x>
<y>180</y> <y>180</y>
<width>141</width> <width>261</width>
<height>31</height> <height>31</height>
</rect> </rect>
</property> </property>
@ -184,7 +184,7 @@ p, li { white-space: pre-wrap; }
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>-40</x> <x>-40</x>
<y>440</y> <y>490</y>
<width>801</width> <width>801</width>
<height>371</height> <height>371</height>
</rect> </rect>
@ -313,7 +313,7 @@ p, li { white-space: pre-wrap; }
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>810</x> <x>810</x>
<y>790</y> <y>760</y>
<width>191</width> <width>191</width>
<height>16</height> <height>16</height>
</rect> </rect>
@ -340,7 +340,7 @@ p, li { white-space: pre-wrap; }
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>810</x> <x>810</x>
<y>820</y> <y>790</y>
<width>191</width> <width>191</width>
<height>16</height> <height>16</height>
</rect> </rect>
@ -432,8 +432,8 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="apcs_spawn_checkBox"> <widget class="QCheckBox" name="apcs_spawn_checkBox">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>470</x> <x>450</x>
<y>400</y> <y>420</y>
<width>251</width> <width>251</width>
<height>31</height> <height>31</height>
</rect> </rect>
@ -444,18 +444,21 @@ p, li { white-space: pre-wrap; }
</font> </font>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Friendly/enemy APCs will drop infantry when reaching a new conflict zone.</string> <string>Friendly/enemy APCs will drop infantry when reaching a new conflict zone. Disables infinite troop pickups from conflict zones (you must pick up existing troops).</string>
</property> </property>
<property name="text"> <property name="text">
<string>APCs Spawn Infantry</string> <string>APCs Spawn Infantry</string>
</property> </property>
<property name="checked">
<bool>true</bool>
</property>
</widget> </widget>
<widget class="QSpinBox" name="inf_spawn_spinBox"> <widget class="QSpinBox" name="inf_spawn_spinBox">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>680</x> <x>670</x>
<y>360</y> <y>340</y>
<width>71</width> <width>51</width>
<height>31</height> <height>31</height>
</rect> </rect>
</property> </property>
@ -517,10 +520,10 @@ p, li { white-space: pre-wrap; }
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>470</x> <x>450</x>
<y>360</y> <y>340</y>
<width>191</width> <width>211</width>
<height>31</height> <height>21</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
@ -528,8 +531,11 @@ p, li { white-space: pre-wrap; }
<pointsize>10</pointsize> <pointsize>10</pointsize>
</font> </font>
</property> </property>
<property name="statusTip">
<string>This value is multiplied by the number of spawn zones in the mission template.</string>
</property>
<property name="text"> <property name="text">
<string>Infantry Groups per zone:</string> <string>Infantry Spawns per zone:</string>
</property> </property>
</widget> </widget>
<widget class="QComboBox" name="slot_template_comboBox"> <widget class="QComboBox" name="slot_template_comboBox">
@ -563,29 +569,11 @@ p, li { white-space: pre-wrap; }
<string>Player Slots</string> <string>Player Slots</string>
</property> </property>
</widget> </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"> <widget class="QCheckBox" name="force_offroad_checkBox">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>810</x> <x>810</x>
<y>760</y> <y>820</y>
<width>191</width> <width>191</width>
<height>16</height> <height>16</height>
</rect> </rect>
@ -596,7 +584,7 @@ p, li { white-space: pre-wrap; }
</font> </font>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>May help prevent long travel times or pathfinding issues. Tip: You can change this dynamically from mission triggers.</string> <string>May help prevent long travel times or pathfinding issues. </string>
</property> </property>
<property name="text"> <property name="text">
<string>Force Offroad</string> <string>Force Offroad</string>
@ -667,6 +655,9 @@ p, li { white-space: pre-wrap; }
<pointsize>11</pointsize> <pointsize>11</pointsize>
</font> </font>
</property> </property>
<property name="statusTip">
<string>Approximate number of enemy attack helicopter group spawns.</string>
</property>
<property name="text"> <property name="text">
<string>Enemy Attack Helicopters</string> <string>Enemy Attack Helicopters</string>
</property> </property>
@ -685,6 +676,9 @@ p, li { white-space: pre-wrap; }
<pointsize>11</pointsize> <pointsize>11</pointsize>
</font> </font>
</property> </property>
<property name="statusTip">
<string>Approximate number of enemy attack plane group spawns.</string>
</property>
<property name="text"> <property name="text">
<string>Enemy Attack Planes</string> <string>Enemy Attack Planes</string>
</property> </property>
@ -740,8 +734,8 @@ p, li { white-space: pre-wrap; }
<widget class="QLabel" name="scenario_label_9"> <widget class="QLabel" name="scenario_label_9">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>740</x> <x>810</x>
<y>490</y> <y>450</y>
<width>171</width> <width>171</width>
<height>31</height> <height>31</height>
</rect> </rect>
@ -758,15 +752,15 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="inf_spawn_voiceovers_checkBox"> <widget class="QCheckBox" name="inf_spawn_voiceovers_checkBox">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>470</x> <x>810</x>
<y>430</y> <y>720</y>
<width>251</width> <width>251</width>
<height>31</height> <height>31</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<pointsize>10</pointsize> <pointsize>9</pointsize>
</font> </font>
</property> </property>
<property name="statusTip"> <property name="statusTip">
@ -870,14 +864,134 @@ p, li { white-space: pre-wrap; }
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="scenario_label_10">
<property name="geometry">
<rect>
<x>140</x>
<y>410</y>
<width>241</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="statusTip">
<string>Approximate number of enemy transport helicopter spawns.</string>
</property>
<property name="text">
<string>Enemy Transport Helicopters</string>
</property>
</widget>
<widget class="QSpinBox" name="e_transport_helos_spinBox">
<property name="geometry">
<rect>
<x>70</x>
<y>410</y>
<width>51</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="statusTip">
<string>Approximate number of enemy transport helicopter spawns.</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>8</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>450</x>
<y>380</y>
<width>191</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="statusTip">
<string>The number of troop drops per transport helicopter flight.</string>
</property>
<property name="text">
<string>Transport Drop Points:</string>
</property>
</widget>
<widget class="QSpinBox" name="troop_drop_spinBox">
<property name="geometry">
<rect>
<x>670</x>
<y>380</y>
<width>51</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="statusTip">
<string>The number of troop drops per transport helicopter flight.</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
<widget class="QCheckBox" name="smoke_pickup_zone_checkBox">
<property name="geometry">
<rect>
<x>810</x>
<y>690</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Infinite troop pickup zones will be marked with blue smoke.</string>
</property>
<property name="text">
<string>Smoke at Troop Pickup Zones</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<zorder>background_label</zorder> <zorder>background_label</zorder>
<zorder>scenario_comboBox</zorder> <zorder>scenario_comboBox</zorder>
<zorder>scenario_label</zorder> <zorder>scenario_label</zorder>
<zorder>generateButton</zorder> <zorder>generateButton</zorder>
<zorder>description_textBrowser</zorder> <zorder>description_textBrowser</zorder>
<zorder>blueforces_comboBox</zorder> <zorder>blueforces_comboBox</zorder>
<zorder>scenario_label_2</zorder> <zorder>blue_forces_label</zorder>
<zorder>scenario_label_3</zorder> <zorder>red_forces_label</zorder>
<zorder>redforces_comboBox</zorder> <zorder>redforces_comboBox</zorder>
<zorder>scenario_hint_label</zorder> <zorder>scenario_hint_label</zorder>
<zorder>forces_hint_label</zorder> <zorder>forces_hint_label</zorder>
@ -896,7 +1010,6 @@ p, li { white-space: pre-wrap; }
<zorder>label</zorder> <zorder>label</zorder>
<zorder>slot_template_comboBox</zorder> <zorder>slot_template_comboBox</zorder>
<zorder>label_2</zorder> <zorder>label_2</zorder>
<zorder>scenario_label_6</zorder>
<zorder>force_offroad_checkBox</zorder> <zorder>force_offroad_checkBox</zorder>
<zorder>defense_checkBox</zorder> <zorder>defense_checkBox</zorder>
<zorder>e_attack_helos_spinBox</zorder> <zorder>e_attack_helos_spinBox</zorder>
@ -910,6 +1023,11 @@ p, li { white-space: pre-wrap; }
<zorder>farp_gunits</zorder> <zorder>farp_gunits</zorder>
<zorder>farp_always</zorder> <zorder>farp_always</zorder>
<zorder>version_label</zorder> <zorder>version_label</zorder>
<zorder>scenario_label_10</zorder>
<zorder>e_transport_helos_spinBox</zorder>
<zorder>label_3</zorder>
<zorder>troop_drop_spinBox</zorder>
<zorder>smoke_pickup_zone_checkBox</zorder>
</widget> </widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
<property name="geometry"> <property name="geometry">
@ -946,6 +1064,11 @@ p, li { white-space: pre-wrap; }
<string>_redforcesSelected</string> <string>_redforcesSelected</string>
</property> </property>
</action> </action>
<action name="action_defensiveModeChanged">
<property name="text">
<string>_defensiveModeChanged</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections> <connections>
@ -960,8 +1083,8 @@ p, li { white-space: pre-wrap; }
<y>591</y> <y>591</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>589</x> <x>-1</x>
<y>409</y> <y>-1</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
@ -981,6 +1104,22 @@ p, li { white-space: pre-wrap; }
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>defense_checkBox</sender>
<signal>stateChanged(int)</signal>
<receiver>action_defensiveModeChanged</receiver>
<slot>trigger()</slot>
<hints>
<hint type="sourcelabel">
<x>150</x>
<y>131</y>
</hint>
<hint type="destinationlabel">
<x>-1</x>
<y>-1</y>
</hint>
</hints>
</connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="farp_buttonGroup"/> <buttongroup name="farp_buttonGroup"/>

View File

@ -11,20 +11,21 @@ import random
class VehicleTemplate: class VehicleTemplate:
class USA: class CombinedJointTaskForcesBlue:
@staticmethod @staticmethod
def invisible_farp(mission, country, position, heading, name, late_activation): def zone_farp(mission, country, farp_country, position, heading, name, late_activation):
# ai air units attack farp with late activation units, so we will set it to enemy coalition. It will be captured when frienly units spawn
farp = mission.farp(country, name, position, hidden=False, dead=False, farp_type=dcs.unit.InvisibleFARP) farp = mission.farp(farp_country, name, position, hidden=False, dead=False, farp_type=dcs.unit.InvisibleFARP)
vg = mission.vehicle_group_platoon( vg = mission.vehicle_group_platoon(
country, country,
name, name + " Static",
[ [
dcs.vehicles.Unarmed.M_818, dcs.vehicles.Unarmed.M_818,
dcs.vehicles.AirDefence.Vulcan, dcs.vehicles.AirDefence.Vulcan,
dcs.vehicles.Unarmed.Ural_375 dcs.vehicles.Unarmed.Ural_375,
dcs.vehicles.Unarmed.M978_HEMTT_Tanker
], ],
position.point_from_heading(45, 7), position.point_from_heading(45, 7),
heading=random.randint(0, 359), heading=random.randint(0, 359),

View File

@ -7,14 +7,12 @@ import random
import RotorOpsGroups import RotorOpsGroups
import RotorOpsUnits import RotorOpsUnits
import time import time
from MissionGenerator import logger
class RotorOpsMission: class RotorOpsMission:
def __init__(self): def __init__(self):
self.m = dcs.mission.Mission() self.m = dcs.mission.Mission()
os.chdir("../") os.chdir("../")
@ -30,7 +28,7 @@ class RotorOpsMission:
self.staging_zones = {} self.staging_zones = {}
self.spawn_zones = {} self.spawn_zones = {}
self.scripts = {} self.scripts = {}
self.res_map = {}
class RotorOpsZone: class RotorOpsZone:
def __init__(self, name: str, flag: int, position: dcs.point, size: int): def __init__(self, name: str, flag: int, position: dcs.point, size: int):
@ -56,7 +54,9 @@ class RotorOpsMission:
for filename in dir_list: for filename in dir_list:
if filename.endswith(".ogg"): if filename.endswith(".ogg"):
#print(filename) #print(filename)
self.m.map_resource.add_resource_file(filename) key = self.m.map_resource.add_resource_file(filename)
self.res_map[filename] = key
#add all of our lua scripts #add all of our lua scripts
os.chdir(script_directory) os.chdir(script_directory)
@ -67,56 +67,86 @@ class RotorOpsMission:
for filename in dir_list: for filename in dir_list:
if filename.endswith(".lua"): if filename.endswith(".lua"):
print("Adding script to mission: " + filename) logger.info("Adding script to mission: " + filename)
self.scripts[filename] = self.m.map_resource.add_resource_file(filename) self.scripts[filename] = self.m.map_resource.add_resource_file(filename)
def getUnitsFromMiz(self, filename, side): def getUnitsFromMiz(self, filename, side):
forces = []
forces = {}
vehicles = []
attack_helos = []
transport_helos = []
attack_planes = []
fighter_planes = []
os.chdir(self.home_dir) os.chdir(self.home_dir)
os.chdir(self.forces_dir + "/" + side) os.chdir(self.forces_dir + "/" + side)
print("Looking for " + side + " Forces files in '", os.getcwd(), "' :") logger.info("Looking for " + side + " Forces files in '" + os.getcwd())
source_mission = dcs.mission.Mission() source_mission = dcs.mission.Mission()
try: try:
source_mission.load_file(filename) source_mission.load_file(filename)
for country_name in source_mission.coalition.get(side).countries: for country_name in source_mission.coalition.get(side).countries:
country_obj = source_mission.coalition.get(side).countries[country_name] country_obj = source_mission.coalition.get(side).countries[country_name]
for vehicle_group in country_obj.vehicle_group: for vehicle_group in country_obj.vehicle_group:
forces.append(vehicle_group) vehicles.append(vehicle_group)
return forces for helicopter_group in country_obj.helicopter_group:
except: if helicopter_group.task == 'CAS':
print("Failed to load units from " + filename) attack_helos.append(helicopter_group)
elif helicopter_group.task == 'Transport':
transport_helos.append(helicopter_group)
for plane_group in country_obj.plane_group:
if plane_group.task == 'CAS':
attack_planes.append(plane_group)
elif plane_group.task == 'CAP':
fighter_planes.append(plane_group)
forces["vehicles"] = vehicles
forces["attack_helos"] = attack_helos
forces["transport_helos"] = transport_helos
forces["attack_planes"] = attack_planes
forces["fighter_planes"] = fighter_planes
return forces
except:
logger.error("Failed to load units from " + filename)
def generateMission(self, options): def generateMission(self, options):
#get the template mission file
os.chdir(self.scenarios_dir) os.chdir(self.scenarios_dir)
print("Looking for mission files in '", os.getcwd(), "' :") logger.info("Looking for mission files in " + os.getcwd())
self.m.load_file(options["scenario_filename"]) self.m.load_file(options["scenario_filename"])
if not self.m.country("Russia") or not self.m.country("USA"): if not self.m.country("Combined Joint Task Forces Red") or not self.m.country("Combined Joint Task Forces Blue"):
failure_msg = "You must include a USA and Russia unit in the scenario template. See the instructions in " + self.scenarios_dir failure_msg = "You must include a CombinedJointTaskForcesBlue and CombinedJointTaskForcesRed unit in the scenario template. See the instructions in " + self.scenarios_dir
return {"success": False, "failure_msg": failure_msg} return {"success": False, "failure_msg": failure_msg}
red_forces = self.getUnitsFromMiz(options["red_forces_filename"], "red") red_forces = self.getUnitsFromMiz(options["red_forces_filename"], "red")
blue_forces = self.getUnitsFromMiz(options["blue_forces_filename"], "blue") blue_forces = self.getUnitsFromMiz(options["blue_forces_filename"], "blue")
# Add coalitions (we may be able to add CJTF here instead of requiring templates to have objects of those coalitions)
self.m.coalition.get("red").add_country(dcs.countries.Russia())
self.m.coalition.get("blue").add_country(dcs.countries.USA())
self.m.add_picture_blue(self.assets_dir + '/briefing1.png')
self.m.add_picture_blue(self.assets_dir + '/briefing2.png')
# add zones to target mission # add zones to target mission
zone_names = ["ALPHA", "BRAVO", "CHARLIE", "DELTA"]
zone_flag = 101
for zone_name in zone_names:
for zone in self.m.triggers.zones():
if zone.name == zone_name:
self.addZone(self.conflict_zones, self.RotorOpsZone(zone_name, zone_flag, zone.position, zone.radius))
zone_flag = zone_flag + 1
for zone in self.m.triggers.zones(): for zone in self.m.triggers.zones():
if zone.name == "ALPHA": if zone.name.rfind("STAGING") >= 0:
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)) self.addZone(self.staging_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
elif zone.name.rfind("SPAWN") >= 0: elif zone.name.rfind("SPAWN") >= 0:
self.addZone(self.spawn_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius)) self.addZone(self.spawn_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
@ -128,28 +158,25 @@ class RotorOpsMission:
blue_zones = self.conflict_zones blue_zones = self.conflict_zones
red_zones = self.staging_zones red_zones = self.staging_zones
#swap airport sides #swap airport sides
blue_airports = self.getCoalitionAirports("blue") self.swapSides(options)
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()
#Populate Red zones with ground units #Populate Red zones with ground units
for zone_name in red_zones: for zone_name in red_zones:
if red_forces: if red_forces["vehicles"]:
self.addGroundGroups(red_zones[zone_name], self.m.country('Russia'), red_forces, options["red_quantity"]) self.addGroundGroups(red_zones[zone_name], self.m.country('Combined Joint Task Forces Red'), red_forces["vehicles"], options["red_quantity"])
#Add blue FARPS #Add red FARPS
if options["zone_farps"] != "farp_never" and not options["defending"]: if options["zone_farps"] != "farp_never" and not options["defending"]:
RotorOpsGroups.VehicleTemplate.USA.invisible_farp(self.m, self.m.country('USA'), RotorOpsGroups.VehicleTemplate.CombinedJointTaskForcesBlue.zone_farp(self.m, self.m.country('Combined Joint Task Forces Blue'),
self.m.country('Combined Joint Task Forces Blue'),
red_zones[zone_name].position, red_zones[zone_name].position,
180, zone_name + " FARP", late_activation=True) 180, zone_name + " FARP", late_activation=True)
if options["zone_protect_sams"]: if options["zone_protect_sams"]:
self.m.vehicle_group( self.m.vehicle_group(
self.m.country('Russia'), self.m.country('Combined Joint Task Forces Red'),
"Static " + zone_name + " Protection SAM", "Static " + zone_name + " Protection SAM",
random.choice(RotorOpsUnits.e_zone_sams), random.choice(RotorOpsUnits.e_zone_sams),
red_zones[zone_name].position, red_zones[zone_name].position,
@ -162,13 +189,19 @@ class RotorOpsMission:
#Populate Blue zones with ground units #Populate Blue zones with ground units
for zone_name in blue_zones: for zone_name in blue_zones:
if blue_forces: if blue_forces["vehicles"]:
self.addGroundGroups(blue_zones[zone_name], self.m.country('USA'), blue_forces, self.addGroundGroups(blue_zones[zone_name], self.m.country('Combined Joint Task Forces Blue'), blue_forces["vehicles"],
options["blue_quantity"]) options["blue_quantity"])
#Add blue FARPS
if options["zone_farps"] != "farp_never" and options["defending"]:
RotorOpsGroups.VehicleTemplate.CombinedJointTaskForcesBlue.zone_farp(self.m, self.m.country('Combined Joint Task Forces Blue'),
self.m.country('Combined Joint Task Forces Blue'),
blue_zones[zone_name].position,
180, zone_name + " FARP", late_activation=False)
#add logistics sites #add logistics sites
if options["crates"] and zone_name in self.staging_zones: if options["crates"] and zone_name in self.staging_zones:
RotorOpsGroups.VehicleTemplate.USA.logistics_site(self.m, self.m.country('USA'), RotorOpsGroups.VehicleTemplate.CombinedJointTaskForcesBlue.logistics_site(self.m, self.m.country('Combined Joint Task Forces Blue'),
blue_zones[zone_name].position, blue_zones[zone_name].position,
180, zone_name) 180, zone_name)
@ -178,7 +211,7 @@ class RotorOpsMission:
if options["zone_protect_sams"] and options["defending"]: if options["zone_protect_sams"] and options["defending"]:
vg = self.m.vehicle_group( vg = self.m.vehicle_group(
self.m.country('USA'), self.m.country('Combined Joint Task Forces Blue'),
"Static " + zone_name + " Protection SAM", "Static " + zone_name + " Protection SAM",
random.choice(RotorOpsUnits.e_zone_sams), random.choice(RotorOpsUnits.e_zone_sams),
blue_zones[zone_name].position, blue_zones[zone_name].position,
@ -188,20 +221,14 @@ class RotorOpsMission:
) )
#Add player slots #Add player slots
if options["slots"] == "Multiple Slots": self.addPlayerHelos(options)
self.addMultiplayerHelos()
else:
for helicopter in dcs.helicopters.helicopter_map:
if helicopter == options["slots"]:
self.addSinglePlayerHelos(dcs.helicopters.helicopter_map[helicopter])
#Add AI Flights #Add AI Flights
self.addFlights(options) self.addFlights(options, red_forces, blue_forces)
#Set the Editor Map View #Set the Editor Map View
self.m.map.position = self.m.terrain.airports[self.getCoalitionAirports("blue")[0]].position self.m.map.position = self.conflict_zones["ALPHA"].position
self.m.map.zoom = 100000 self.m.map.zoom = 100000
#add files and triggers necessary for RotorOps.lua script #add files and triggers necessary for RotorOps.lua script
@ -209,7 +236,6 @@ class RotorOpsMission:
self.scriptTriggerSetup(options) self.scriptTriggerSetup(options)
#Save the mission file #Save the mission file
print(self.m.triggers.zones())
os.chdir(self.output_dir) os.chdir(self.output_dir)
output_filename = options["scenario_filename"].removesuffix('.miz') + " " + time.strftime('%a%H%M%S') + '.miz' output_filename = options["scenario_filename"].removesuffix('.miz') + " " + time.strftime('%a%H%M%S') + '.miz'
success = self.m.save(output_filename) success = self.m.save(output_filename)
@ -224,14 +250,11 @@ class RotorOpsMission:
if dcs.vehicles.vehicle_map[unit.type]: if dcs.vehicles.vehicle_map[unit.type]:
unit_types.append(dcs.vehicles.vehicle_map[unit.type]) unit_types.append(dcs.vehicles.vehicle_map[unit.type])
country = self.m.country(_country.name) country = self.m.country(_country.name)
#pos1 = zone.position.point_from_heading(5, 200)
#for i in range(0, quantity):
self.m.vehicle_group_platoon( self.m.vehicle_group_platoon(
country, country,
zone.name + '-GND ' + str(a+1), zone.name + '-GND ' + str(a+1),
unit_types, unit_types,
zone.position.random_point_within(zone.size / 1.2, 100), zone.position.random_point_within(zone.size / 1.2, 100),
#pos1.random_point_within(zone.size / 2.5, 100),
heading=random.randint(0, 359), heading=random.randint(0, 359),
formation=dcs.unitgroup.VehicleGroup.Formation.Scattered, formation=dcs.unitgroup.VehicleGroup.Formation.Scattered,
) )
@ -239,66 +262,159 @@ class RotorOpsMission:
def getCoalitionAirports(self, side: str): def getCoalitionAirports(self, side: str):
coalition_airports = [] coalition_airports = []
primary_airport = None
shortest_dist = 1000000
for airport_name in self.m.terrain.airports: for airport_name in self.m.terrain.airports:
airportobj = self.m.terrain.airports[airport_name] airportobj = self.m.terrain.airports[airport_name]
if airportobj.coalition == str.upper(side): if airportobj.coalition == str.upper(side):
coalition_airports.append(airport_name)
return coalition_airports
def getParking(self, airport, aircraft): coalition_airports.append(airportobj)
slot = airport.free_parking_slot(aircraft)
slots = airport.free_parking_slots(aircraft) start = self.staging_zones[list(self.staging_zones)[0]]
if slot: dist_from_start = dcs.mapping._distance(airportobj.position.x, airportobj.position.y, start.position.x, start.position.y)
return airport
else: if dist_from_start < shortest_dist:
print("No parking available for " + aircraft.id + " at " + airport.name) primary_airport = airportobj
return None shortest_dist = dist_from_start
return coalition_airports, primary_airport
def getParking(self, airport, aircraft, alt_airports=None, group_size=1):
if len(airport.free_parking_slots(aircraft)) >= group_size:
if not (aircraft.id in dcs.planes.plane_map and len(airport.runways) == 0):
return airport
for airport in alt_airports:
if len(airport.free_parking_slots(aircraft)) >= group_size:
if not (aircraft.id in dcs.planes.plane_map and len(airport.runways) == 0):
return airport
logger.warn("No parking available for " + aircraft.id)
return None
#Find parking spots on FARPs and carriers #Find parking spots on FARPs and carriers
def getUnitParking(self, aircraft): def getUnitParking(self, aircraft):
return return
def addSinglePlayerHelos(self, helotype): def swapSides(self, options):
carrier = self.m.country("USA").find_ship_group(name="HELO_CARRIER") #Swap airports
farp = self.m.country("USA").find_static_group("HELO_FARP")
friendly_airports = self.getCoalitionAirports("blue")
if carrier: blue_airports, primary_blue = self.getCoalitionAirports("blue")
fg = self.m.flight_group_from_unit(self.m.country('USA'), "CARRIER " + helotype.id, helotype, carrier, dcs.task.CAS, group_size=2) red_airports, primary_red = self.getCoalitionAirports("red")
elif farp: for airport in blue_airports:
fg = self.m.flight_group_from_unit(self.m.country('USA'), "FARP " + helotype.id, helotype, farp, dcs.task.CAS, group_size=2) self.m.terrain.airports[airport.name].set_red()
fg.units[0].position = fg.units[0].position.point_from_heading(90, 30) for airport in red_airports:
self.m.terrain.airports[airport.name].set_blue()
# invisible farps need manual unit placement for multiple units combinedJointTaskForcesBlue = self.m.country("Combined Joint Task Forces Blue")
if farp.units[0].type == 'Invisible FARP': combinedJointTaskForcesRed = self.m.country("Combined Joint Task Forces Red")
fg.points[0].action = dcs.point.PointAction.FromGroundArea
fg.points[0].type = "TakeOffGround"
fg.units[0].position = fg.units[0].position.point_from_heading(0, 30)
else:
for airport_name in friendly_airports: #Swap ships
fg = self.m.flight_group_from_airport(self.m.country('USA'), airport_name + " " + helotype.id, helotype,
self.getParking(self.m.terrain.airports[airport_name], helotype), group_size=2) blue_ships = combinedJointTaskForcesBlue.ship_group.copy()
fg.units[0].set_player() red_ships = combinedJointTaskForcesRed.ship_group.copy()
for group in blue_ships:
group.points[0].tasks.append(dcs.task.OptROE(dcs.task.OptROE.Values.ReturnFire))
combinedJointTaskForcesRed.add_ship_group(group)
combinedJointTaskForcesBlue.ship_group.remove(group)
for group in red_ships:
combinedJointTaskForcesBlue.add_ship_group(group)
combinedJointTaskForcesRed.ship_group.remove(group)
def addMultiplayerHelos(self): #Swap statics
carrier = self.m.country("USA").find_ship_group(name="HELO_CARRIER")
farp = self.m.country("USA").find_static_group("HELO_FARP") blue_statics = combinedJointTaskForcesBlue.static_group.copy()
friendly_airports = self.getCoalitionAirports("blue") red_statics = combinedJointTaskForcesRed.static_group.copy()
for group in blue_statics:
combinedJointTaskForcesBlue.static_group.remove(group)
combinedJointTaskForcesRed.add_static_group(group)
for group in red_statics:
combinedJointTaskForcesRed.static_group.remove(group)
combinedJointTaskForcesBlue.add_static_group(group)
#Swap vehicles
blue_vehicles = combinedJointTaskForcesBlue.vehicle_group.copy()
red_vehicles = combinedJointTaskForcesRed.vehicle_group.copy()
for group in blue_vehicles:
combinedJointTaskForcesBlue.vehicle_group.remove(group)
combinedJointTaskForcesRed.add_vehicle_group(group)
for group in red_vehicles:
combinedJointTaskForcesRed.vehicle_group.remove(group)
combinedJointTaskForcesBlue.add_vehicle_group(group)
#Swap planes
blue_planes = combinedJointTaskForcesBlue.plane_group.copy()
red_planes = combinedJointTaskForcesRed.plane_group.copy()
for group in blue_planes:
combinedJointTaskForcesBlue.plane_group.remove(group)
combinedJointTaskForcesRed.add_plane_group(group)
for group in red_planes:
combinedJointTaskForcesRed.plane_group.remove(group)
combinedJointTaskForcesBlue.add_plane_group(group)
# Swap helicopters
blue_helos = combinedJointTaskForcesBlue.helicopter_group.copy()
red_helos = combinedJointTaskForcesRed.helicopter_group.copy()
for group in blue_helos:
combinedJointTaskForcesBlue.helicopter_group.remove(group)
combinedJointTaskForcesRed.add_helicopter_group(group)
for group in red_helos:
combinedJointTaskForcesRed.helicopter_group.remove(group)
combinedJointTaskForcesBlue.add_helicopter_group(group)
def addPlayerHelos(self, options):
client_helos = RotorOpsUnits.client_helos
for helicopter in dcs.helicopters.helicopter_map:
if helicopter == options["slots"]:
client_helos = [dcs.helicopters.helicopter_map[helicopter]]
#find friendly carriers and farps
carrier = self.m.country("Combined Joint Task Forces Blue").find_ship_group(name="HELO_CARRIER")
if not carrier:
carrier = self.m.country("Combined Joint Task Forces Blue").find_ship_group(name="HELO_CARRIER_1")
farp = self.m.country("Combined Joint Task Forces Blue").find_static_group("HELO_FARP")
if not farp:
farp = self.m.country("Combined Joint Task Forces Blue").find_static_group("HELO_FARP_1")
friendly_airports, primary_f_airport = self.getCoalitionAirports("blue")
heading = 0 heading = 0
for helotype in RotorOpsUnits.client_helos: group_size = 1
if len(client_helos) == 1:
group_size = 2 #add a wingman if singleplayer
for helotype in client_helos:
if carrier: if carrier:
fg = self.m.flight_group_from_unit(self.m.country('USA'), "CARRIER " + helotype.id, helotype, carrier, fg = self.m.flight_group_from_unit(self.m.country('Combined Joint Task Forces Blue'), "CARRIER " + helotype.id, helotype, carrier,
dcs.task.CAS, group_size=1) dcs.task.CAS, group_size=group_size)
elif farp: elif farp:
fg = self.m.flight_group_from_unit(self.m.country('USA'), "FARP " + helotype.id, helotype, farp, fg = self.m.flight_group_from_unit(self.m.country('Combined Joint Task Forces Blue'), "FARP " + helotype.id, helotype, farp,
dcs.task.CAS, group_size=1) dcs.task.CAS, group_size=group_size)
#invisible farps need manual unit placement for multiple units #invisible farps need manual unit placement for multiple units
if farp.units[0].type == 'Invisible FARP': if farp.units[0].type == 'Invisible FARP':
@ -307,10 +423,14 @@ class RotorOpsMission:
fg.units[0].position = fg.units[0].position.point_from_heading(heading, 30) fg.units[0].position = fg.units[0].position.point_from_heading(heading, 30)
heading += 90 heading += 90
else: else:
for airport_name in friendly_airports: fg = self.m.flight_group_from_airport(self.m.country('Combined Joint Task Forces Blue'), primary_f_airport.name + " " + helotype.id, helotype,
fg = self.m.flight_group_from_airport(self.m.country('USA'), airport_name + " " + helotype.id, helotype, self.getParking(primary_f_airport, helotype), group_size=group_size)
self.getParking(self.m.terrain.airports[airport_name], helotype), group_size=1)
fg.units[0].set_client() fg.units[0].set_client()
fg.load_task_default_loadout(dcs.task.CAS)
#setup wingman for single player
if len(fg.units) == 2:
fg.units[1].skill = dcs.unit.Skill.High
class TrainingScenario(): class TrainingScenario():
@ -324,41 +444,75 @@ class RotorOpsMission:
race_dist = random.randrange(80 * 1000, 120 * 1000) race_dist = random.randrange(80 * 1000, 120 * 1000)
return dcs.mapping.Point(x1, y1), heading, race_dist return dcs.mapping.Point(x1, y1), heading, race_dist
@staticmethod
def perpRacetrack(enemy_heading, friendly_pt):
heading = enemy_heading + random.randrange(70,110)
race_dist = random.randrange(40 * 1000, 80 * 1000)
center_pt = dcs.mapping.point_from_heading(friendly_pt.x, friendly_pt.y, enemy_heading - random.randrange(140, 220), 10000)
pt1 = dcs.mapping.point_from_heading(center_pt[0], center_pt[1], enemy_heading - 90, random.randrange(20 * 1000, 40 * 1000))
return dcs.mapping.Point(pt1[0], pt1[1]), heading, race_dist
def addFlights(self, options, red_forces, blue_forces):
combinedJointTaskForcesBlue = self.m.country(dcs.countries.CombinedJointTaskForcesBlue.name)
combinedJointTaskForcesRed = self.m.country(dcs.countries.CombinedJointTaskForcesRed.name)
friendly_airports, primary_f_airport = self.getCoalitionAirports("blue")
enemy_airports, primary_e_airport = self.getCoalitionAirports("red")
def addFlights(self, options): #find enemy carriers and farps
usa = self.m.country(dcs.countries.USA.name) carrier = self.m.country("Combined Joint Task Forces Red").find_ship_group(name="HELO_CARRIER")
russia = self.m.country(dcs.countries.Russia.name) if not carrier:
friendly_airport = self.m.terrain.airports[self.getCoalitionAirports("blue")[0]] carrier = self.m.country("Combined Joint Task Forces Red").find_ship_group(name="HELO_CARRIER_1")
enemy_airport = self.m.terrain.airports[self.getCoalitionAirports("red")[0]]
farp = self.m.country("Combined Joint Task Forces Red").find_static_group("HELO_FARP")
if not farp:
farp = self.m.country("Combined Joint Task Forces Red").find_static_group("HELO_FARP_1")
orbit_rect = dcs.mapping.Rectangle( e_airport_heading = dcs.mapping.heading_between_points(
int(friendly_airport.position.x), int(friendly_airport.position.y - 100 * 1000), int(friendly_airport.position.x - 100 * 1000), friendly_airports[0].position.x, friendly_airports[0].position.y, enemy_airports[0].position.x, primary_e_airport.position.y
int(friendly_airport.position.y)) )
e_airport_distance = dcs.mapping._distance(
primary_f_airport.position.x, primary_f_airport.position.y, primary_f_airport.position.x, primary_f_airport.position.y
)
if options["f_awacs"]: if options["f_awacs"]:
awacs_name = "AWACS" awacs_name = "AWACS"
awacs_freq = 266 awacs_freq = 266
pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect) #pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position)
awacs = self.m.awacs_flight( awacs = self.m.awacs_flight(
usa, combinedJointTaskForcesBlue,
awacs_name, awacs_name,
plane_type=dcs.planes.E_3A, plane_type=dcs.planes.E_3A,
airport=self.getParking(friendly_airport, dcs.planes.E_3A), airport=self.getParking(primary_f_airport, dcs.planes.E_3A, friendly_airports),
position=pos, position=pos,
race_distance=race_dist, heading=heading, race_distance=race_dist, heading=heading,
altitude=random.randrange(4000, 5500, 100), frequency=awacs_freq) altitude=random.randrange(4000, 5500, 100), frequency=awacs_freq)
# AWACS Escort flight
source_plane = None
if blue_forces["fighter_planes"]:
source_group = random.choice(blue_forces["fighter_planes"])
source_plane = source_group.units[0]
plane_type = source_plane.unit_type
else:
plane_type = dcs.countries.CombinedJointTaskForcesBlue.Plane.F_15C
awacs_escort = self.m.escort_flight( awacs_escort = self.m.escort_flight(
usa, "AWACS Escort", combinedJointTaskForcesBlue, "AWACS Escort",
dcs.countries.USA.Plane.F_15C, plane_type,
airport=self.getParking(friendly_airport, dcs.countries.USA.Plane.F_15C), airport=self.getParking(primary_f_airport, plane_type, friendly_airports),
group_to_escort=awacs, group_to_escort=awacs,
group_size=2) group_size=2)
awacs_escort.load_loadout("Combat Air Patrol") #not working for f-15
awacs_escort.points[0].tasks.append(dcs.task.OptROE(dcs.task.OptROE.Values.WeaponFree))
if source_plane:
for unit in awacs_escort.units:
unit.pylons = source_plane.pylons
unit.livery_id = source_plane.livery_id
#add text to mission briefing with radio freq #add text to mission briefing with radio freq
briefing = self.m.description_text() + "\n\n" + awacs_name + " " + str(awacs_freq) + ".00 " + "\n" briefing = self.m.description_text() + "\n\n" + awacs_name + " " + str(awacs_freq) + ".00 " + "\n"
@ -371,12 +525,13 @@ class RotorOpsMission:
t2_name = "Tanker KC_135 Boom" t2_name = "Tanker KC_135 Boom"
t2_freq = 256 t2_freq = 256
t2_tac = "101Y" t2_tac = "101Y"
pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect) #pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position)
refuel_net = self.m.refuel_flight( refuel_net = self.m.refuel_flight(
usa, combinedJointTaskForcesBlue,
t1_name, t1_name,
dcs.planes.KC130, dcs.planes.KC130,
airport=self.getParking(friendly_airport, dcs.planes.KC130), airport=self.getParking(primary_f_airport, dcs.planes.KC130, friendly_airports),
position=pos, position=pos,
race_distance=race_dist, race_distance=race_dist,
heading=heading, heading=heading,
@ -386,12 +541,13 @@ class RotorOpsMission:
frequency=t1_freq, frequency=t1_freq,
tacanchannel=t1_tac) tacanchannel=t1_tac)
pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect) #pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position)
refuel_rod = self.m.refuel_flight( refuel_rod = self.m.refuel_flight(
usa, combinedJointTaskForcesBlue,
t2_name, t2_name,
dcs.planes.KC_135, dcs.planes.KC_135,
airport=self.getParking(friendly_airport, dcs.planes.KC_135), airport=self.getParking(primary_f_airport, dcs.planes.KC_135, friendly_airports),
position=pos, position=pos,
race_distance=race_dist, heading=heading, race_distance=race_dist, heading=heading,
altitude=random.randrange(4000, 5500, 100), altitude=random.randrange(4000, 5500, 100),
@ -403,52 +559,141 @@ class RotorOpsMission:
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" 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) self.m.set_description_text(briefing)
def zone_attack(fg, unit_type): def zone_attack(fg, airport):
fg.set_skill(dcs.unit.Skill.Random) fg.set_skill(dcs.unit.Skill.High)
fg.late_activation = True fg.late_activation = True
fg.points[0].tasks.append(dcs.task.OptROE(0))
#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"]: if options["defending"]:
for zone_name in self.conflict_zones: for zone_name in self.conflict_zones:
fg.add_waypoint(self.conflict_zones[zone_name].position, 1000) fg.add_waypoint(self.conflict_zones[zone_name].position, 1000)
else: else:
for zone_name in reversed(self.conflict_zones): for zone_name in reversed(self.conflict_zones):
fg.add_waypoint(self.conflict_zones[zone_name].position, 1000) fg.add_waypoint(self.conflict_zones[zone_name].position, 1000)
fg.add_runway_waypoint(enemy_airport) if hasattr(airport, 'runways'):
fg.land_at(enemy_airport) fg.add_runway_waypoint(airport)
if airport:
fg.land_at(airport)
fg.points[0].tasks.append(dcs.task.OptReactOnThreat(dcs.task.OptReactOnThreat.Values.EvadeFire))
fg.points[0].tasks.append(dcs.task.OptROE(dcs.task.OptROE.Values.OpenFire))
if options["e_attack_helos"]: if options["e_attack_helos"]:
helo = random.choice(RotorOpsUnits.e_attack_helos) source_helo = None
afg = self.m.flight_group_from_airport( if red_forces["attack_helos"]:
russia, source_group = random.choice(red_forces["attack_helos"])
"Enemy Attack Helicopters", source_helo = source_group.units[0]
helo, helo_type = source_helo.unit_type
airport=enemy_airport, group_size = len(source_group.units)
maintask=dcs.task.CAS, if group_size > 2:
start_type=dcs.mission.StartType.Cold, group_size = 2
group_size=2)
zone_attack(afg, helo) else:
group_size = 2
helo_type = random.choice(RotorOpsUnits.e_attack_helos)
airport = self.getParking(primary_e_airport, helo_type, enemy_airports, group_size)
if carrier:
afg = self.m.flight_group_from_unit(
combinedJointTaskForcesRed,
"Enemy Attack Helicopters",
helo_type,
carrier,
maintask=dcs.task.CAS,
start_type=dcs.mission.StartType.Cold,
group_size=group_size)
zone_attack(afg, carrier)
elif farp:
afg = self.m.flight_group_from_unit(
combinedJointTaskForcesRed,
"Enemy Attack Helicopters",
helo_type,
farp,
maintask=dcs.task.CAS,
start_type=dcs.mission.StartType.Cold,
group_size=group_size)
zone_attack(afg, farp)
elif airport:
afg = self.m.flight_group_from_airport(
combinedJointTaskForcesRed,
"Enemy Attack Helicopters",
helo_type,
airport=airport,
maintask=dcs.task.CAS,
start_type=dcs.mission.StartType.Cold,
group_size=group_size)
zone_attack(afg, airport)
else:
return
if source_helo:
for unit in afg.units:
unit.pylons = source_helo.pylons
unit.livery_id = source_helo.livery_id
if options["e_attack_planes"]: if options["e_attack_planes"]:
plane = random.choice(RotorOpsUnits.e_attack_planes) source_plane = None
afg = self.m.flight_group_from_airport( if red_forces["attack_planes"]:
russia, "Enemy Attack Planes", plane["type"], source_group = random.choice(red_forces["attack_planes"])
airport=enemy_airport, source_plane = source_group.units[0]
maintask=dcs.task.CAS, plane_type = source_plane.unit_type
start_type=dcs.mission.StartType.Cold, group_size = len(source_group.units)
group_size=2) if group_size > 2:
zone_attack(afg, plane) group_size = 2
else:
group_size = 2
plane_type = random.choice(RotorOpsUnits.e_attack_planes)
airport = self.getParking(primary_e_airport, plane_type, enemy_airports, group_size)
if airport:
afg = self.m.flight_group_from_airport(
combinedJointTaskForcesRed, "Enemy Attack Planes", plane_type,
airport=airport,
maintask=dcs.task.CAS,
start_type=dcs.mission.StartType.Cold,
group_size=group_size)
zone_attack(afg, airport)
if source_plane:
for unit in afg.units:
unit.pylons = source_plane.pylons
unit.livery_id = source_plane.livery_id
if options["e_transport_helos"]:
source_helo = None
if red_forces["transport_helos"]:
source_group = random.choice(red_forces["transport_helos"])
source_helo = source_group.units[0]
helo_type = source_helo.unit_type
group_size = len(source_group.units)
if group_size > 2:
group_size = 2
else:
group_size = 1
helo_type = random.choice(RotorOpsUnits.e_transport_helos)
airport = self.getParking(primary_e_airport, helo_type, enemy_airports, group_size)
if airport:
afg = self.m.flight_group_from_airport(
combinedJointTaskForcesRed, "Enemy Transport Helicopters", helo_type,
airport=airport,
maintask=dcs.task.Transport,
start_type=dcs.mission.StartType.Cold,
group_size=group_size)
afg.late_activation = True
afg.units[0].skill = dcs.unit.Skill.Excellent
if source_helo:
for unit in afg.units:
unit.pylons = source_helo.pylons
unit.livery_id = source_helo.livery_id
def scriptTriggerSetup(self, options): def scriptTriggerSetup(self, options):
@ -464,8 +709,8 @@ class RotorOpsMission:
trig.actions.append(dcs.action.DoScriptFile(self.scripts["Splash_Damage_2_0.lua"])) trig.actions.append(dcs.action.DoScriptFile(self.scripts["Splash_Damage_2_0.lua"]))
trig.actions.append(dcs.action.DoScriptFile(self.scripts["CTLD.lua"])) trig.actions.append(dcs.action.DoScriptFile(self.scripts["CTLD.lua"]))
trig.actions.append(dcs.action.DoScriptFile(self.scripts["RotorOps.lua"])) trig.actions.append(dcs.action.DoScriptFile(self.scripts["RotorOps.lua"]))
trig.actions.append(dcs.action.DoScript(dcs.action.String(( script = ""
"--OPTIONS HERE!\n\n" + script = ("--OPTIONS HERE!\n\n" +
"RotorOps.CTLD_crates = " + lb("crates") + "\n\n" + "RotorOps.CTLD_crates = " + lb("crates") + "\n\n" +
"RotorOps.CTLD_sound_effects = true\n\n" + "RotorOps.CTLD_sound_effects = true\n\n" +
"RotorOps.force_offroad = " + lb("force_offroad") + "\n\n" + "RotorOps.force_offroad = " + lb("force_offroad") + "\n\n" +
@ -473,7 +718,10 @@ class RotorOpsMission:
"RotorOps.zone_status_display = " + lb("game_display") + "\n\n" + "RotorOps.zone_status_display = " + lb("game_display") + "\n\n" +
"RotorOps.inf_spawn_messages = " + lb("inf_spawn_msgs") + "\n\n" + "RotorOps.inf_spawn_messages = " + lb("inf_spawn_msgs") + "\n\n" +
"RotorOps.inf_spawns_per_zone = " + lb("inf_spawn_qty") + "\n\n" + "RotorOps.inf_spawns_per_zone = " + lb("inf_spawn_qty") + "\n\n" +
"RotorOps.apcs_spawn_infantry = " + lb("apc_spawns_inf") + " \n\n")))) "RotorOps.apcs_spawn_infantry = " + lb("apc_spawns_inf") + " \n\n")
if not options["smoke_pickup_zones"]:
script = script + 'RotorOps.pickup_zone_smoke = "none"\n\n'
trig.actions.append(dcs.action.DoScript(dcs.action.String((script))))
self.m.triggerrules.triggers.append(trig) self.m.triggerrules.triggers.append(trig)
#Add the second trigger #Add the second trigger
@ -510,31 +758,37 @@ class RotorOpsMission:
self.m.triggerrules.triggers.append(z_sams_trig) self.m.triggerrules.triggers.append(z_sams_trig)
#Zone FARPS always #Zone FARPS always
if options["zone_farps"] == "farp_always" and not options["defending"] and index > 0: if options["zone_farps"] == "farp_always" and not options["defending"]:
for index, zone_name in enumerate(self.conflict_zones): for index, zone_name in enumerate(self.conflict_zones):
if index > 0: if index > 0:
previous_zone = list(self.conflict_zones)[index - 1] previous_zone = list(self.conflict_zones)[index - 1]
if not self.m.country("USA").find_group(previous_zone + " FARP"): if not self.m.country("Combined Joint Task Forces Blue").find_group(previous_zone + " FARP Static"):
continue continue
z_farps_trig = dcs.triggers.TriggerOnce(comment="Activate " + previous_zone + " FARP") z_farps_trig = dcs.triggers.TriggerOnce(comment="Activate " + previous_zone + " FARP")
z_farps_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1)) z_farps_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1))
z_farps_trig.actions.append(dcs.action.ActivateGroup(self.m.country("USA").find_group(previous_zone + " FARP").id)) z_farps_trig.actions.append(dcs.action.ActivateGroup(self.m.country("Combined Joint Task Forces Blue").find_group(previous_zone + " FARP Static").id))
#z_farps_trig.actions.append(dcs.action.SoundToAll(str(self.res_map['forward_base_established.ogg'])))
z_farps_trig.actions.append(dcs.action.DoScript(dcs.action.String(
"RotorOps.farpEstablished(" + str(index) + ")")))
self.m.triggerrules.triggers.append(z_farps_trig) self.m.triggerrules.triggers.append(z_farps_trig)
#Zone FARPS conditional on staged units remaining #Zone FARPS conditional on staged units remaining
if options["zone_farps"] == "farp_gunits": if options["zone_farps"] == "farp_gunits" and not options["defending"]:
for index, zone_name in enumerate(self.conflict_zones): for index, zone_name in enumerate(self.conflict_zones):
if index > 0: if index > 0:
previous_zone = list(self.conflict_zones)[index - 1] previous_zone = list(self.conflict_zones)[index - 1]
if not self.m.country("USA").find_group(previous_zone + " FARP"): if not self.m.country("Combined Joint Task Forces Blue").find_group(previous_zone + " FARP Static"):
continue continue
z_farps_trig = dcs.triggers.TriggerOnce(comment= "Activate " + previous_zone + " FARP") z_farps_trig = dcs.triggers.TriggerOnce(comment= "Activate " + previous_zone + " FARP")
z_farps_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1)) z_farps_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1))
z_farps_trig.rules.append(dcs.condition.FlagIsMore(111, 20)) z_farps_trig.rules.append(dcs.condition.FlagIsMore(111, 20))
z_farps_trig.actions.append(dcs.action.DoScript(dcs.action.String("--The 100 flag indicates which zone is active. The 111 flag value is the percentage of staged units remaining"))) z_farps_trig.actions.append(dcs.action.DoScript(dcs.action.String("--The 100 flag indicates which zone is active. The 111 flag value is the percentage of staged units remaining")))
z_farps_trig.actions.append( z_farps_trig.actions.append(
dcs.action.ActivateGroup(self.m.country("USA").find_group(previous_zone + " FARP").id)) dcs.action.ActivateGroup(self.m.country("Combined Joint Task Forces Blue").find_group(previous_zone + " FARP Static").id))
#z_farps_trig.actions.append(dcs.action.SoundToAll(str(self.res_map['forward_base_established.ogg'])))
z_farps_trig.actions.append(dcs.action.DoScript(dcs.action.String(
"RotorOps.farpEstablished(" + str(index) + ")")))
self.m.triggerrules.triggers.append(z_farps_trig) self.m.triggerrules.triggers.append(z_farps_trig)
@ -546,7 +800,7 @@ class RotorOpsMission:
z_weak_trig = dcs.triggers.TriggerOnce(comment=zone.name + " Attack Helo") z_weak_trig = dcs.triggers.TriggerOnce(comment=zone.name + " Attack Helo")
z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone.flag, 1)) z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone.flag, 1))
z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 90))) z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 90)))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("---Flag value represents the percentage of defending ground units remaining in zone. "))) z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("---Flag " + str(zone.flag) + " value represents the percentage of defending ground units remaining in zone. ")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.spawnAttackHelos()"))) z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.spawnAttackHelos()")))
self.m.triggerrules.triggers.append(z_weak_trig) self.m.triggerrules.triggers.append(z_weak_trig)
@ -557,10 +811,24 @@ class RotorOpsMission:
z_weak_trig = dcs.triggers.TriggerOnce(comment=zone.name + " Attack Plane") z_weak_trig = dcs.triggers.TriggerOnce(comment=zone.name + " Attack Plane")
z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone.flag, 1)) z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone.flag, 1))
z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 90))) z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 90)))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("---Flag value represents the percentage of defending ground units remaining in zone. "))) z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("---Flag " + str(zone.flag) + " value represents the percentage of defending ground units remaining in zone. ")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.spawnAttackPlanes()"))) z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.spawnAttackPlanes()")))
self.m.triggerrules.triggers.append(z_weak_trig) self.m.triggerrules.triggers.append(z_weak_trig)
#Add transport helos triggers
for index in range(options["e_transport_helos"]):
random_zone_index = random.randrange(1, len(self.conflict_zones))
random_zone_obj = list(self.conflict_zones.items())[random_zone_index]
zone = random_zone_obj[1]
z_weak_trig = dcs.triggers.TriggerOnce(comment=zone.name + " Transport Helo")
z_weak_trig.rules.append(dcs.condition.FlagEquals(game_flag, random_zone_index + 1))
z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 100)))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String(
"---Flag " + str(game_flag) + " value represents the index of the active zone. ")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("---Flag " + str(zone.flag) + " value represents the percentage of defending ground units remaining in zone. ")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.spawnTranspHelos(8," + str(options["transport_drop_qty"]) + ")")))
self.m.triggerrules.triggers.append(z_weak_trig)
#Add game won/lost triggers #Add game won/lost triggers
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict WON") trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict WON")
trig.rules.append(dcs.condition.FlagEquals(game_flag, 99)) trig.rules.append(dcs.condition.FlagEquals(game_flag, 99))

View File

@ -14,15 +14,14 @@ e_attack_helos = [
] ]
e_transport_helos = [ e_transport_helos = [
dcs.helicopters.Mi_26, #dcs.helicopters.Mi_26,
dcs.helicopters.Mi_24P, #dcs.helicopters.Mi_24P,
dcs.helicopters.Mi_8MT, #dcs.helicopters.Mi_8MT,
dcs.helicopters.CH_47D,
] ]
e_attack_planes = [ 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"}, dcs.planes.A_10C,
#{'type': dcs.planes.Su_25, 'loadout': "RKB-250*8,R-60M*2"},
{'type': dcs.planes.A_10C, 'loadout': ""}
] ]
e_zone_sams = [ e_zone_sams = [

View File

@ -3,7 +3,7 @@ You can add your own scenarios in this directory and they will appear in the mis
A scenario .miz file MUST have: A scenario .miz file MUST have:
1) Between 1-4 trigger zones called "ALPHA", "BRAVO", "CHARLIE", "DELTA" 1) Between 1-4 circular trigger zones called "ALPHA", "BRAVO", "CHARLIE", "DELTA"
2) At least one trigger zone with a name that starts with "STAGING". 2) At least one trigger zone with a name that starts with "STAGING".
3) A blue airport (recommend somewhere near/on-side your staging zone). 3) A blue airport (recommend somewhere near/on-side your staging zone).
4) A red airport (recommend somewhere near/on-side your last conflict zone). 4) A red airport (recommend somewhere near/on-side your last conflict zone).
@ -13,16 +13,17 @@ A scenario .miz file MUST have:
Optional: Optional:
7) USA FARP called "HELO_FARP" for automatic player placement. 7) USA FARP called "HELO_FARP" for automatic player placement. (Strongly suggest using immortal and invisible options for support vehicles, and add "static" to the group name to keep them from moving)
8) USA Carrier called "HELO_CARRIER" for automatic player placement. 8) USA Carrier called "HELO_CARRIER" for automatic player placement.
9) Infantry spawn zones inside conflict zones. Add near buildings to simulate infantry hiding within. Name trigger zones like "ALPHA_SPAWN", "ALPHA_SPAWN_2, etc. 9) Infantry spawn zones inside conflict zones. Add near buildings to simulate infantry hiding within. Name trigger zones like "ALPHA_SPAWN", "ALPHA_SPAWN_2, etc.
Testing:
You should test your scenarios to ensure that vehicals move between zones as you expect. In some circumstances, vehicles may not be able to calculate a valid route to the next zone, and they will stop. Make sure they can route correctly by testing your scenario in fast forward, with few defending units. You can also get a good idea as to how long your scenario will take to complete.
Tips: Tips:
-Position the center of conflict zones over an open area, as this position may be used to spawn units. -Position the center of conflict zones over an open area, as this position will be used to spawn units and FARPs.
-Position the center of staging zones over an open area of at least 1000 x 1000ft to provide space for logistics units. -Position the center of staging zones over an open area of at least 1000 x 1000ft to provide space for logistics zones.
Position the center of staging zones over an open area of at least 1000 x 1000ft to provide space for logistics units.
-For very scenery dense areas like forests or urban areas, smaller conflict zone sizes are highly recommended (eg 4000ft radius). A zone center near a roadway may also help keep units moving smoothly. -For very scenery dense areas like forests or urban areas, smaller conflict zone sizes are highly recommended (eg 4000ft radius). A zone center near a roadway may also help keep units moving smoothly.
-The conflict game type can be played with blue forces on defense. In this mode the last conflict zone is the only troop pickup zone. -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. -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.
@ -35,4 +36,6 @@ Position the center of staging zones over an open area of at least 1000 x 1000ft
-Friendly AWACs and tankers will be placed at the blue airport if parking is available, otherwise they will start in the air. -Friendly AWACs and tankers will be placed at the blue airport if parking is available, otherwise they will start in the air.
-Enemy helicopters and planes will spawn at the red airport. -Enemy helicopters and planes will spawn at the red airport.
-Late activation FARPs might be useful for rearming far from the player spawn point. -Late activation FARPs might be useful for rearming far from the player spawn point.
-In "Defense" or with "Swap sides" option, USA and Russia ships, helicopters, planes, and ground units will swap countries. Static objects may not. Test it out.
-Turn off or limit civilian road traffic.
-Pay attention to rivers and bridges, as a far away bridge crossing may break routing if it's the only way across. See the testing notes above.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 KiB

BIN
Generator/requirements.txt Normal file

Binary file not shown.

View File

@ -0,0 +1,39 @@
import dcs
import os
def main():
filename = "source.miz"
print("Attempting to extract units from " + filename + " relative to 'HELO_FARP' initial point.")
source_mission = dcs.mission.Mission()
source_mission.load_file(filename)
fo = open("units.txt", "w")
usa = source_mission.country("USA")
initial_point = usa.find_static_group("HELO_FARP").position
def p(mystring):
fo.write(mystring + '\n')
print(mystring)
group_types = [usa.static_group, usa.vehicle_group, usa.helicopter_group, usa.plane_group, usa.ship_group]
for group_type in group_types:
for group in group_type:
for unit in group.units:
print(str(unit.position.x))
x_rel = initial_point.x - unit.position.x
y_rel = initial_point.y - unit.position.y
heading = unit.heading
p(unit.type)
p("x: " + str(round(x_rel, 7)))
p("y: " + str(round(y_rel, 7)))
p("h: " + str(round(heading, 2)))
p('\n')
fo.close()
main()

Binary file not shown.

View File

@ -1,8 +1,9 @@
RotorOps = {} RotorOps = {}
RotorOps.version = "1.2.5" RotorOps.version = "1.2.6"
local debug = true local debug = true
---[[ROTOROPS OPTIONS]]--- ---[[ROTOROPS OPTIONS]]---
--- Protip: change these options from the mission editor rather than changing the script file itself. See documentation on github for details. --- Protip: change these options from the mission editor rather than changing the script file itself. See documentation on github for details.
@ -15,6 +16,7 @@ RotorOps.max_units_left = 0 --allow clearing the zone when a few units are left
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.auto_push = true --should attacking ground units move to the next zone after clearing?
RotorOps.defending_vehicles_disperse = true
RotorOps.inf_spawns_avail = 0 --this is the number of infantry group spawn events remaining in the active zone 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_chance = 25 -- 0-100 the chance of spawning infantry in an active zone spawn zone, per 'assessUnitsInZone' loop (10 seconds)
@ -28,6 +30,7 @@ RotorOps.transports = {'UH-1H', 'Mi-8MT', 'Mi-24P', 'SA342M', 'SA342L', 'SA342Mi
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 = "Static" --include this somewhere in a group name to exclude the group from being tasked in the active zone RotorOps.exclude_ai_group_name = "Static" --include this somewhere in a group name to exclude the group from being tasked in the active zone
RotorOps.pickup_zone_smoke = "blue"
---[[END OF OPTIONS]]--- ---[[END OF OPTIONS]]---
@ -67,6 +70,7 @@ local inf_spawn_zones = {}
local cooldown = { local cooldown = {
["attack_helo_msg"] = 0, ["attack_helo_msg"] = 0,
["attack_plane_msg"] = 0, ["attack_plane_msg"] = 0,
["trans_helo_msg"] = 0,
} }
@ -164,13 +168,23 @@ RotorOps.gameMsgs = {
{'ENEMY TROOPS IN THE ACTIVE!', 'e_infantry_spawn5.ogg'}, {'ENEMY TROOPS IN THE ACTIVE!', 'e_infantry_spawn5.ogg'},
{'VISUAL ON ENEMY TROOPS!', 'e_infantry_spawn6.ogg'}, {'VISUAL ON ENEMY TROOPS!', 'e_infantry_spawn6.ogg'},
}, },
farp_established = {
{'NEW FARP AVAILABLE!', 'forward_base_established.ogg'},
{'NEW FARP AT ALPHA!', 'forward_base_established.ogg'},
{'NEW FARP AT BRAVO!', 'forward_base_established.ogg'},
{'NEW FARP AT CHARLIE!', 'forward_base_established.ogg'},
{'NEW FARP AT DELTA!', 'forward_base_established.ogg'},
},
transp_helos_toff = {
{'ENEMY TRANSPORT HELICOPTERS INBOUND!', 'enemy_chopper_inbound.ogg'},
},
} }
local sound_effects = { local sound_effects = {
["troop_pickup"] = {'troops_load_ao.ogg', 'troops_load_ready.ogg', 'troops_load_to_action.ogg',force_offroad = true}, ["troop_pickup"] = {'troops_load_ao.ogg', 'troops_load_ready.ogg', 'troops_load_to_action.ogg',},
["troop_dropoff"] = {'troops_unload_thanks.ogg', 'troops_unload_everybody_off.ogg', 'troops_unload_get_off.ogg', 'troops_unload_here_we_go.ogg', 'troops_unload_moving_out.ogg',}, ["troop_dropoff"] = {'troops_unload_thanks.ogg', 'troops_unload_everybody_off.ogg', 'troops_unload_get_off.ogg', 'troops_unload_here_we_go.ogg', 'troops_unload_moving_out.ogg',},
} }
@ -184,15 +198,15 @@ end
function RotorOps.eventHandler:onEvent(event) function RotorOps.eventHandler:onEvent(event)
---ENGINE STARTUP EVENTS ---ENGINE STARTUP EVENTS
if (world.event.S_EVENT_ENGINE_STARTUP == event.id) then --play some sound files when a player starts engines if (world.event.S_EVENT_ENGINE_STARTUP == event.id) then --play some sound files when a player starts engines
local initiator = event.initiator:getGroup():getID() local initiator = event.initiator:getGroup():getID()
if #event.initiator:getGroup():getUnits() == 1 then --if there are no other units in the player flight group (preventing duplicated messages for ai wingman flights) if #event.initiator:getGroup():getUnits() == 1 then --if there are no other units in the player flight group (preventing duplicated messages for ai wingman flights)
if RotorOps.defending then if RotorOps.defending then
trigger.action.outSoundForGroup(initiator , RotorOps.gameMsgs.enemy_pushing[RotorOps.active_zone_index + 1][2]) trigger.action.outSoundForGroup(initiator , RotorOps.gameMsgs.enemy_pushing[RotorOps.active_zone_index + 1][2])
else else
trigger.action.outSoundForGroup(initiator , RotorOps.gameMsgs.push[RotorOps.active_zone_index + 1][2]) trigger.action.outSoundForGroup(initiator , RotorOps.gameMsgs.push[RotorOps.active_zone_index + 1][2])
end end
end end
end end
@ -219,8 +233,28 @@ function RotorOps.eventHandler:onEvent(event)
end end
end end
if initiator_name == "Enemy Transport Helicopters" then --we're using mist clone now so group name will not match
env.info("Transport helicopter took off")
if ((RotorOps.getTime() - cooldown["trans_helo_msg"]) > 90) then
timer.scheduleFunction(function()RotorOps.gameMsg(RotorOps.gameMsgs.transp_helos_toff) end, {}, timer.getTime() + 1)
cooldown["trans_helo_msg"] = RotorOps.getTime()
else
env.warning("RotorOps transport helo message skipped")
end
end
end end
---BASE CAPTURE EVENTS --doesn't work with FARPs..
if (world.event.S_EVENT_BASE_CAPTURED == event.id) then
env.info("Base captured")
if (event.place:getCoalition() == 2) then
env.info("Blue forces captured a base via place attribute")
end
end
end end
@ -258,7 +292,8 @@ end
local function debugTable(table) local function debugTable(table)
trigger.action.outText("dbg: ".. mist.utils.tableShow(table), 5) --trigger.action.outText("dbg: ".. mist.utils.tableShow(table), 5)
env.info("ROTOROPS_DEBUG: ".. mist.utils.tableShow(table))
end end
@ -357,6 +392,7 @@ local function processMsgBuffer(vars)
if #game_message_buffer > 0 then if #game_message_buffer > 0 then
local message = table.remove(game_message_buffer, 1) local message = table.remove(game_message_buffer, 1)
trigger.action.outText(message[1], 10, true) trigger.action.outText(message[1], 10, true)
env.info("RotorOps: "..message[1])
if RotorOps.voice_overs then if RotorOps.voice_overs then
trigger.action.outSound(message[2]) trigger.action.outSound(message[2])
end end
@ -422,45 +458,7 @@ 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)
@ -470,6 +468,7 @@ function RotorOps.deployTroops(quantity, target_group, announce)
else else
target_group_obj = target_group target_group_obj = target_group
end end
debugMsg("DeployTroops on group: "..target_group_obj:getName())
local valid_unit = RotorOps.getValidUnitFromGroup(target_group_obj) local valid_unit = RotorOps.getValidUnitFromGroup(target_group_obj)
if not valid_unit then return end if not valid_unit then return end
local coalition = valid_unit:getCoalition() local coalition = valid_unit:getCoalition()
@ -811,6 +810,7 @@ function RotorOps.aiExecute(vars)
local speed = RotorOps.ground_speed local speed = RotorOps.ground_speed
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 end
@ -882,6 +882,14 @@ function RotorOps.assessUnitsInZone(var)
RotorOps.aiTask(group, "clear_zone", RotorOps.active_zone) RotorOps.aiTask(group, "clear_zone", RotorOps.active_zone)
end end
end end
for index, group in pairs(RotorOps.ai_defending_vehicle_groups) do
if group then
Group.getByName(group):getController():setOption(AI.Option.Ground.id.DISPERSE_ON_ATTACK , RotorOps.defending_vehicles_disperse)
end
end
--FIRES ONCE PER ZONE ACTIVATION --FIRES ONCE PER ZONE ACTIVATION
@ -945,7 +953,7 @@ function RotorOps.assessUnitsInZone(var)
local percent_staged_remain = 0 local percent_staged_remain = 0
percent_staged_remain = math.floor((#staged_units_remaining / #RotorOps.staged_units) * 100) percent_staged_remain = math.floor((#staged_units_remaining / #RotorOps.staged_units) * 100)
trigger.action.setUserFlag(RotorOps.staged_units_flag, percent_staged_remain) trigger.action.setUserFlag(RotorOps.staged_units_flag, percent_staged_remain)
debugMsg("Staged units remaining: "..percent_staged_remain.."%") debugMsg("Staged units remaining percent: "..percent_staged_remain.."%")
--is the game finished? --is the game finished?
@ -1156,7 +1164,7 @@ function RotorOps.setActiveZone(new_index)
if not RotorOps.defending then if not RotorOps.defending then
if old_index > 0 then if old_index > 0 and RotorOps.apcs_spawn_infantry == false then
ctld.activatePickupZone(RotorOps.zones[old_index].name) --make the captured zone a pickup zone ctld.activatePickupZone(RotorOps.zones[old_index].name) --make the captured zone a pickup zone
end end
ctld.deactivatePickupZone(RotorOps.zones[new_index].name) ctld.deactivatePickupZone(RotorOps.zones[new_index].name)
@ -1176,7 +1184,8 @@ function RotorOps.setActiveZone(new_index)
local staged_groups = RotorOps.groupsFromUnits(RotorOps.staged_units) local staged_groups = RotorOps.groupsFromUnits(RotorOps.staged_units)
for index, group in pairs(staged_groups) do for index, group in pairs(staged_groups) do
RotorOps.aiTask(group,"move_to_active_zone", RotorOps.zones[RotorOps.active_zone_index].name) --send vehicles to next zone; use move_to_active_zone so units don't get stuck if the active zone moves before they arrive timer.scheduleFunction(function()RotorOps.aiTask(group,"move_to_active_zone", RotorOps.zones[RotorOps.active_zone_index].name) end, {}, timer.getTime() + index) --add a second between calling aitask
--RotorOps.aiTask(group,"move_to_active_zone", RotorOps.zones[RotorOps.active_zone_index].name) --send vehicles to next zone; use move_to_active_zone so units don't get stuck if the active zone moves before they arrive
end end
@ -1196,6 +1205,7 @@ function RotorOps.setupCTLD()
return return
end end
--ctld.Debug = false
ctld.enableCrates = RotorOps.CTLD_crates ctld.enableCrates = RotorOps.CTLD_crates
ctld.enabledFOBBuilding = false ctld.enabledFOBBuilding = false
ctld.JTAC_lock = "vehicle" ctld.JTAC_lock = "vehicle"
@ -1217,6 +1227,7 @@ function RotorOps.setupCTLD()
["UH-1H"] = 10, ["UH-1H"] = 10,
["Mi-8MT"] = 24, ["Mi-8MT"] = 24,
["Mi-24P"] = 8, ["Mi-24P"] = 8,
["UH-60L"] = 11,
} }
ctld.loadableGroups = { ctld.loadableGroups = {
@ -1228,8 +1239,10 @@ function RotorOps.setupCTLD()
{name = "JTAC Group (4)", inf = 3, jtac = 1 }, {name = "JTAC Group (4)", inf = 3, jtac = 1 },
{name = "Small Platoon (16)", inf = 9, mg = 3, at = 3, aa = 1 }, {name = "Small Platoon (16)", inf = 9, mg = 3, at = 3, aa = 1 },
{name = "Platoon (24)", inf = 10, mg = 5, at = 6, aa = 3 }, {name = "Platoon (24)", inf = 10, mg = 5, at = 6, aa = 3 },
}
}
end end
@ -1250,7 +1263,7 @@ function RotorOps.addZone(_name, _zone_defenders_flag)
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()
RotorOps.addPickupZone(_name, "blue", -1, "no", 0) RotorOps.addPickupZone(_name, RotorOps.pickup_zone_smoke, -1, "no", 2)
end end
function RotorOps.stagingZone(_name) function RotorOps.stagingZone(_name)
@ -1258,7 +1271,7 @@ function RotorOps.stagingZone(_name)
trigger.action.outText(_name.." trigger zone missing! Check RotorOps setup!", 60) trigger.action.outText(_name.." trigger zone missing! Check RotorOps setup!", 60)
env.warning(_name.." trigger zone missing! Check RotorOps setup!") env.warning(_name.." trigger zone missing! Check RotorOps setup!")
end end
RotorOps.addPickupZone(_name, "blue", -1, "no", 0) RotorOps.addPickupZone(_name, RotorOps.pickup_zone_smoke, -1, "no", 0)
RotorOps.staging_zone = _name RotorOps.staging_zone = _name
end end
@ -1332,33 +1345,155 @@ function RotorOps.startConflict()
end end
function RotorOps.triggerSpawn(groupName, msg) function RotorOps.triggerSpawn(groupName, msg, resume_task)
local group = Group.getByName(groupName) local group = Group.getByName(groupName)
if not group then
env.warning("RotorOps tried to spawn "..groupName.." but it doesn't exist.")
return nil
end
if group and group:isExist() == true and #group:getUnits() > 0 and group:getUnits()[1]:getLife() > 1 and group:getUnits()[1]:isActive() then if group and group:isExist() == true and #group:getUnits() > 0 and group:getUnits()[1]:getLife() > 1 and group:getUnits()[1]:isActive() then
env.info("RotorOps tried to respawn "..groupName.." but it's already active.") env.info("RotorOps tried to respawn "..groupName.." but it's already active.")
return nil
else else
local new_group = mist.respawnGroup(groupName, true) local new_group = mist.respawnGroup(groupName, resume_task)
if new_group then if new_group then
RotorOps.gameMsg(msg) if msg ~= nil then
RotorOps.gameMsg(msg)
end
env.info("RotorOps spawned "..groupName) env.info("RotorOps spawned "..groupName)
return new_group return new_group
end end
end end
return nil
end end
function RotorOps.spawnAttackHelos() function RotorOps.spawnAttackHelos()
RotorOps.triggerSpawn("Enemy Attack Helicopters", RotorOps.gameMsgs.attack_helos_prep) RotorOps.triggerSpawn("Enemy Attack Helicopters", RotorOps.gameMsgs.attack_helos_prep, true)
end end
function RotorOps.spawnAttackPlanes() function RotorOps.spawnAttackPlanes()
RotorOps.triggerSpawn("Enemy Attack Planes", RotorOps.gameMsgs.attack_planes_prep) RotorOps.triggerSpawn("Enemy Attack Planes", RotorOps.gameMsgs.attack_planes_prep, true)
end end
function RotorOps.farpEstablished(index)
env.info("RotorOps FARP established at "..RotorOps.zones[index].name)
timer.scheduleFunction(function()RotorOps.gameMsg(RotorOps.gameMsgs.farp_established, index) end, {}, timer.getTime() + 15)
end
function RotorOps.getEnemyZones()
local enemy_zones = {}
if RotorOps.defending then
for index, zone in pairs(RotorOps.zones) do
if index <= RotorOps.active_zone_index then
enemy_zones[#enemy_zones + 1] = zone.name
end
end
else --not defending
for index, zone in pairs(RotorOps.zones) do
if index >= RotorOps.active_zone_index then
enemy_zones[#enemy_zones + 1] = zone.name
end
end
end
debugMsg("Got enemy zones:")
debugTable(enemy_zones)
return enemy_zones
end
function RotorOps.spawnTranspHelos(troops, max_drops)
local script_string = [[local this_grp = ...
this_grp:getController():setOption(AI.Option.Air.id.REACTION_ON_THREAT , AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE)
this_grp:getController():setOption(AI.Option.Air.id.FLARE_USING , AI.Option.Air.val.FLARE_USING.WHEN_FLYING_NEAR_ENEMIES)]]
local setOptions = {
id = 'WrappedAction',
params = {
action = {
id = 'Script',
params = {
command = script_string,
},
},
},
}
local dropTroops = {
id = 'WrappedAction',
params = {
action = {
id = 'Script',
params = {
command = 'RotorOps.deployTroops('..troops..', ...)',
},
},
},
}
local group = Group.getByName("Enemy Transport Helicopters")
local initial_point = group:getUnits()[1]:getPoint()
local gp = mist.getGroupData("Enemy Transport Helicopters")
--debugTable(gp)
local drop_zones = RotorOps.getEnemyZones()
if RotorOps.defending then
drop_zones = {RotorOps.active_zone}
end
gp.route = {points = {}}
gp.route.points[1] = mist.heli.buildWP(initial_point, initial, 'flyover', 0, 0, 'agl')
gp.route.points[2] = mist.heli.buildWP(initial_point, initial, 'flyover', 100, 100, 'agl')
gp.route.points[2].task = setOptions
local failsafe = 100
local drop_qty = 0
while drop_qty < max_drops do
for i = 1, 10 do --pick some random points to evaluate
local zone_name = drop_zones[math.random(#drop_zones)]
local zone_point = trigger.misc.getZone(zone_name).point
local drop_point = mist.getRandomPointInZone(zone_name, 300)
if mist.isTerrainValid(drop_point, {'LAND', 'ROAD'}) == true then --if the point looks like a good drop point
gp.route.points[#gp.route.points + 1] = mist.heli.buildWP(zone_point, 'flyover', 100, 400, 'agl')
gp.route.points[#gp.route.points + 1] = mist.heli.buildWP(zone_point, 'flyover', 20, 200, 'agl')
gp.route.points[#gp.route.points + 1] = mist.heli.buildWP(drop_point, 'turning point', 10, 70, 'agl')
gp.route.points[#gp.route.points].task = dropTroops
drop_qty = drop_qty + 1
break
end
end
failsafe = failsafe - 1
if failsafe < 1 then
env.error("ROTOROPS: FINDING DROP POINTS TOOK TOO LONG")
break
end
end
gp.route.points[#gp.route.points + 1] = mist.heli.buildWP(initial_point, 'flyover', 100, 400, 'agl')
gp.clone = true
local new_group_data = mist.dynAdd(gp) --returns a mist group data table
debugTable(new_group_data)
-- local new_group = Group.getByName(new_group_data.groupName)
-- local grp_controller = new_group:getController() --controller for aircraft can be group or unit level
-- grp_controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT , AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE)
-- grp_controller:setOption(AI.Option.Air.id.FLARE_USING , AI.Option.Air.val.FLARE_USING.WHEN_FLYING_NEAR_ENEMIES)
env.info("ROTOROPS: TRANSPORT HELICOPTER DEPARTING WITH "..drop_qty.." PLANNED TROOP DROPS.")
end

1
license.txt Normal file
View File

@ -0,0 +1 @@
Use of this software or derivitives of this software is not permitted on 24/7 public multiplayer servers without permission. Scheduled events are excepted.

Binary file not shown.