Merge pull request #20 from spencershepard/develop

Develop
This commit is contained in:
spencershepard 2022-02-06 16:15:42 -08:00 committed by GitHub
commit 02f496a406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 606 additions and 142 deletions

Binary file not shown.

View File

@ -13,6 +13,9 @@ from PyQt5.QtWidgets import (
from PyQt5 import QtGui
from MissionGeneratorUI import Ui_MainWindow
maj_version = 0
minor_version = 3
version_string = str(maj_version) + "." + str(minor_version)
scenarios = []
red_forces_files = []
blue_forces_files = []
@ -43,6 +46,8 @@ class Window(QMainWindow, Ui_MainWindow):
self.statusbar.setStyleSheet(
"QStatusBar{padding-left:5px;color:black;font-weight:bold;}")
self.version_label.setText("Version " + version_string)
def connectSignalsSlots(self):
# self.action_Exit.triggered.connect(self.close)
@ -155,14 +160,14 @@ class Window(QMainWindow, Ui_MainWindow):
"crates": self.logistics_crates_checkBox.isChecked(),
"f_awacs": self.awacs_checkBox.isChecked(),
"f_tankers": self.tankers_checkBox.isChecked(),
"smoke_zone": self.smoke_checkBox.isChecked(),
"voiceovers": self.voiceovers_checkBox.isChecked(),
"force_offroad": self.force_offroad_checkBox.isChecked(),
"game_display": self.game_status_checkBox.isChecked(),
"defending": self.defense_checkBox.isChecked(),
"slots": self.slot_template_comboBox.currentText(),
"smoke_zone": self.smoke_checkBox.isChecked(),
"zone_protect_sams": self.zone_sams_checkBox.isChecked(),
"zone_farps": self.farp_buttonGroup.checkedButton().objectName(),
"inf_spawn_msgs": self.inf_spawn_voiceovers_checkBox.isChecked(),
}
os.chdir(self.m.home_dir + '/Generator')
n = ROps.RotorOpsMission()

View File

@ -73,7 +73,7 @@ class Ui_MainWindow(object):
self.redforces_comboBox.setGeometry(QtCore.QRect(170, 230, 291, 31))
self.redforces_comboBox.setObjectName("redforces_comboBox")
self.background_label = QtWidgets.QLabel(self.centralwidget)
self.background_label.setGeometry(QtCore.QRect(-30, 490, 801, 371))
self.background_label.setGeometry(QtCore.QRect(-40, 440, 801, 371))
self.background_label.setAutoFillBackground(False)
self.background_label.setStyleSheet("")
self.background_label.setText("")
@ -93,7 +93,7 @@ class Ui_MainWindow(object):
font.setPointSize(12)
self.blueqty_spinBox.setFont(font)
self.blueqty_spinBox.setMinimum(0)
self.blueqty_spinBox.setMaximum(50)
self.blueqty_spinBox.setMaximum(8)
self.blueqty_spinBox.setProperty("value", 3)
self.blueqty_spinBox.setObjectName("blueqty_spinBox")
self.redqty_spinBox = QtWidgets.QSpinBox(self.centralwidget)
@ -102,7 +102,7 @@ class Ui_MainWindow(object):
font.setPointSize(12)
self.redqty_spinBox.setFont(font)
self.redqty_spinBox.setMinimum(0)
self.redqty_spinBox.setMaximum(50)
self.redqty_spinBox.setMaximum(8)
self.redqty_spinBox.setProperty("value", 2)
self.redqty_spinBox.setObjectName("redqty_spinBox")
self.scenario_label_4 = QtWidgets.QLabel(self.centralwidget)
@ -113,7 +113,7 @@ class Ui_MainWindow(object):
self.scenario_label_4.setAlignment(QtCore.Qt.AlignCenter)
self.scenario_label_4.setObjectName("scenario_label_4")
self.game_status_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.game_status_checkBox.setGeometry(QtCore.QRect(1000, 590, 191, 16))
self.game_status_checkBox.setGeometry(QtCore.QRect(810, 790, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.game_status_checkBox.setFont(font)
@ -121,20 +121,21 @@ class Ui_MainWindow(object):
self.game_status_checkBox.setTristate(False)
self.game_status_checkBox.setObjectName("game_status_checkBox")
self.voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.voiceovers_checkBox.setGeometry(QtCore.QRect(1000, 650, 191, 16))
self.voiceovers_checkBox.setGeometry(QtCore.QRect(810, 820, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.voiceovers_checkBox.setFont(font)
self.voiceovers_checkBox.setChecked(True)
self.voiceovers_checkBox.setObjectName("voiceovers_checkBox")
self.logistics_crates_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.logistics_crates_checkBox.setGeometry(QtCore.QRect(970, 390, 251, 31))
self.logistics_crates_checkBox.setGeometry(QtCore.QRect(920, 320, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.logistics_crates_checkBox.setFont(font)
self.logistics_crates_checkBox.setChecked(True)
self.logistics_crates_checkBox.setObjectName("logistics_crates_checkBox")
self.awacs_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.awacs_checkBox.setGeometry(QtCore.QRect(970, 420, 251, 31))
self.awacs_checkBox.setGeometry(QtCore.QRect(920, 350, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.awacs_checkBox.setFont(font)
@ -142,34 +143,27 @@ class Ui_MainWindow(object):
self.awacs_checkBox.setChecked(True)
self.awacs_checkBox.setObjectName("awacs_checkBox")
self.tankers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.tankers_checkBox.setGeometry(QtCore.QRect(970, 450, 251, 31))
self.tankers_checkBox.setGeometry(QtCore.QRect(920, 380, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.tankers_checkBox.setFont(font)
self.tankers_checkBox.setChecked(True)
self.tankers_checkBox.setObjectName("tankers_checkBox")
self.apcs_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(500, 400, 251, 31))
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(470, 400, 251, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.apcs_spawn_checkBox.setFont(font)
self.apcs_spawn_checkBox.setObjectName("apcs_spawn_checkBox")
self.inf_spawn_spinBox = QtWidgets.QSpinBox(self.centralwidget)
self.inf_spawn_spinBox.setGeometry(QtCore.QRect(710, 360, 71, 31))
self.inf_spawn_spinBox.setGeometry(QtCore.QRect(680, 360, 71, 31))
font = QtGui.QFont()
font.setPointSize(12)
self.inf_spawn_spinBox.setFont(font)
self.inf_spawn_spinBox.setMinimum(0)
self.inf_spawn_spinBox.setMaximum(50)
self.inf_spawn_spinBox.setMaximum(20)
self.inf_spawn_spinBox.setProperty("value", 2)
self.inf_spawn_spinBox.setObjectName("inf_spawn_spinBox")
self.smoke_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.smoke_checkBox.setGeometry(QtCore.QRect(1000, 620, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.smoke_checkBox.setFont(font)
self.smoke_checkBox.setChecked(True)
self.smoke_checkBox.setObjectName("smoke_checkBox")
self.scenario_label_5 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_5.setGeometry(QtCore.QRect(50, 260, 101, 31))
font = QtGui.QFont()
@ -182,28 +176,28 @@ class Ui_MainWindow(object):
self.forces_hint_label_2.setAlignment(QtCore.Qt.AlignCenter)
self.forces_hint_label_2.setObjectName("forces_hint_label_2")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(500, 360, 191, 31))
self.label.setGeometry(QtCore.QRect(470, 360, 191, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.label.setFont(font)
self.label.setObjectName("label")
self.slot_template_comboBox = QtWidgets.QComboBox(self.centralwidget)
self.slot_template_comboBox.setGeometry(QtCore.QRect(870, 700, 291, 31))
self.slot_template_comboBox.setGeometry(QtCore.QRect(870, 640, 291, 31))
self.slot_template_comboBox.setObjectName("slot_template_comboBox")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(750, 700, 111, 31))
self.label_2.setGeometry(QtCore.QRect(750, 640, 111, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.scenario_label_6 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_6.setGeometry(QtCore.QRect(500, 320, 141, 31))
self.scenario_label_6.setGeometry(QtCore.QRect(470, 320, 141, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.scenario_label_6.setFont(font)
self.scenario_label_6.setObjectName("scenario_label_6")
self.force_offroad_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.force_offroad_checkBox.setGeometry(QtCore.QRect(1000, 560, 191, 16))
self.force_offroad_checkBox.setGeometry(QtCore.QRect(810, 760, 191, 16))
font = QtGui.QFont()
font.setPointSize(9)
self.force_offroad_checkBox.setFont(font)
@ -222,17 +216,17 @@ class Ui_MainWindow(object):
font.setPointSize(12)
self.e_attack_helos_spinBox.setFont(font)
self.e_attack_helos_spinBox.setMinimum(0)
self.e_attack_helos_spinBox.setMaximum(50)
self.e_attack_helos_spinBox.setMaximum(8)
self.e_attack_helos_spinBox.setProperty("value", 2)
self.e_attack_helos_spinBox.setObjectName("e_attack_helos_spinBox")
self.scenario_label_7 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_7.setGeometry(QtCore.QRect(140, 330, 231, 31))
self.scenario_label_7.setGeometry(QtCore.QRect(140, 330, 211, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.scenario_label_7.setFont(font)
self.scenario_label_7.setObjectName("scenario_label_7")
self.scenario_label_8 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_8.setGeometry(QtCore.QRect(140, 370, 231, 31))
self.scenario_label_8.setGeometry(QtCore.QRect(140, 370, 201, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.scenario_label_8.setFont(font)
@ -243,15 +237,56 @@ class Ui_MainWindow(object):
font.setPointSize(12)
self.e_attack_planes_spinBox.setFont(font)
self.e_attack_planes_spinBox.setMinimum(0)
self.e_attack_planes_spinBox.setMaximum(50)
self.e_attack_planes_spinBox.setMaximum(8)
self.e_attack_planes_spinBox.setProperty("value", 1)
self.e_attack_planes_spinBox.setObjectName("e_attack_planes_spinBox")
self.zone_sams_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.zone_sams_checkBox.setGeometry(QtCore.QRect(970, 480, 201, 31))
self.zone_sams_checkBox.setGeometry(QtCore.QRect(920, 410, 201, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.zone_sams_checkBox.setFont(font)
self.zone_sams_checkBox.setObjectName("zone_sams_checkBox")
self.scenario_label_9 = QtWidgets.QLabel(self.centralwidget)
self.scenario_label_9.setGeometry(QtCore.QRect(740, 490, 171, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.scenario_label_9.setFont(font)
self.scenario_label_9.setObjectName("scenario_label_9")
self.inf_spawn_voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.inf_spawn_voiceovers_checkBox.setGeometry(QtCore.QRect(470, 430, 251, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.inf_spawn_voiceovers_checkBox.setFont(font)
self.inf_spawn_voiceovers_checkBox.setChecked(True)
self.inf_spawn_voiceovers_checkBox.setObjectName("inf_spawn_voiceovers_checkBox")
self.farp_never = QtWidgets.QRadioButton(self.centralwidget)
self.farp_never.setGeometry(QtCore.QRect(950, 500, 95, 20))
font = QtGui.QFont()
font.setPointSize(9)
self.farp_never.setFont(font)
self.farp_never.setObjectName("farp_never")
self.farp_buttonGroup = QtWidgets.QButtonGroup(MainWindow)
self.farp_buttonGroup.setObjectName("farp_buttonGroup")
self.farp_buttonGroup.addButton(self.farp_never)
self.farp_gunits = QtWidgets.QRadioButton(self.centralwidget)
self.farp_gunits.setGeometry(QtCore.QRect(950, 530, 221, 21))
font = QtGui.QFont()
font.setPointSize(9)
self.farp_gunits.setFont(font)
self.farp_gunits.setChecked(True)
self.farp_gunits.setObjectName("farp_gunits")
self.farp_buttonGroup.addButton(self.farp_gunits)
self.farp_always = QtWidgets.QRadioButton(self.centralwidget)
self.farp_always.setGeometry(QtCore.QRect(950, 560, 221, 21))
font = QtGui.QFont()
font.setPointSize(9)
self.farp_always.setFont(font)
self.farp_always.setObjectName("farp_always")
self.farp_buttonGroup.addButton(self.farp_always)
self.version_label = QtWidgets.QLabel(self.centralwidget)
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.setObjectName("version_label")
self.background_label.raise_()
self.scenario_comboBox.raise_()
self.scenario_label.raise_()
@ -273,7 +308,6 @@ class Ui_MainWindow(object):
self.tankers_checkBox.raise_()
self.apcs_spawn_checkBox.raise_()
self.inf_spawn_spinBox.raise_()
self.smoke_checkBox.raise_()
self.scenario_label_5.raise_()
self.forces_hint_label_2.raise_()
self.label.raise_()
@ -287,6 +321,12 @@ class Ui_MainWindow(object):
self.scenario_label_8.raise_()
self.e_attack_planes_spinBox.raise_()
self.zone_sams_checkBox.raise_()
self.scenario_label_9.raise_()
self.inf_spawn_voiceovers_checkBox.raise_()
self.farp_never.raise_()
self.farp_gunits.raise_()
self.farp_always.raise_()
self.version_label.raise_()
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1209, 26))
@ -334,15 +374,13 @@ class Ui_MainWindow(object):
self.game_status_checkBox.setText(_translate("MainWindow", "Game Status Display"))
self.voiceovers_checkBox.setStatusTip(_translate("MainWindow", "Voiceovers from the ground commander. Helps keep focus on the active zone."))
self.voiceovers_checkBox.setText(_translate("MainWindow", "Voiceovers"))
self.logistics_crates_checkBox.setStatusTip(_translate("MainWindow", "Enable CTLD logistics crates for building ground units and air defenses."))
self.logistics_crates_checkBox.setText(_translate("MainWindow", "Logistics Crates"))
self.logistics_crates_checkBox.setStatusTip(_translate("MainWindow", "Enable CTLD logistics crates for building ground units and air defenses. Pickup logistics containers to create new logistics sites."))
self.logistics_crates_checkBox.setText(_translate("MainWindow", "Logistics"))
self.awacs_checkBox.setText(_translate("MainWindow", "Friendly AWACS"))
self.tankers_checkBox.setText(_translate("MainWindow", "Friendly Tankers"))
self.apcs_spawn_checkBox.setStatusTip(_translate("MainWindow", "Friendly/enemy APCs will drop infantry when reaching a new conflict zone."))
self.apcs_spawn_checkBox.setText(_translate("MainWindow", "APCs Spawn Infantry"))
self.inf_spawn_spinBox.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
self.smoke_checkBox.setStatusTip(_translate("MainWindow", "Not yet implemented."))
self.smoke_checkBox.setText(_translate("MainWindow", "Smoke Active Zone"))
self.scenario_label_5.setText(_translate("MainWindow", "Groups Per Zone"))
self.forces_hint_label_2.setText(_translate("MainWindow", "Forces templates are .miz files in \'Generator/Forces\'"))
self.label.setText(_translate("MainWindow", "Infantry Groups per zone:"))
@ -358,6 +396,16 @@ class Ui_MainWindow(object):
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.setText(_translate("MainWindow", "Inactive Zone SAMs"))
self.scenario_label_9.setText(_translate("MainWindow", "Zone FARP Conditions:"))
self.inf_spawn_voiceovers_checkBox.setStatusTip(_translate("MainWindow", "Friendly/enemy APCs will drop infantry when reaching a new conflict zone."))
self.inf_spawn_voiceovers_checkBox.setText(_translate("MainWindow", "Voiceovers on Infantry Spawn"))
self.farp_never.setStatusTip(_translate("MainWindow", "Never spawn FARPs in defeated conflict zones."))
self.farp_never.setText(_translate("MainWindow", "Never"))
self.farp_gunits.setStatusTip(_translate("MainWindow", "Only spawn FARPs in defeated conflict zones if we have sufficient ground units remaining."))
self.farp_gunits.setText(_translate("MainWindow", "20% Ground Units Remaining"))
self.farp_always.setStatusTip(_translate("MainWindow", "Always spawn a FARP in defeated conflict zones."))
self.farp_always.setText(_translate("MainWindow", "Always"))
self.version_label.setText(_translate("MainWindow", "Version string"))
self.action_generateMission.setText(_translate("MainWindow", "_generateMission"))
self.action_scenarioSelected.setText(_translate("MainWindow", "_scenarioSelected"))
self.action_blueforcesSelected.setText(_translate("MainWindow", "_blueforcesSelected"))

View File

@ -183,8 +183,8 @@ p, li { white-space: pre-wrap; }
<widget class="QLabel" name="background_label">
<property name="geometry">
<rect>
<x>-30</x>
<y>490</y>
<x>-40</x>
<y>440</y>
<width>801</width>
<height>371</height>
</rect>
@ -255,7 +255,7 @@ p, li { white-space: pre-wrap; }
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
<number>8</number>
</property>
<property name="value">
<number>3</number>
@ -282,7 +282,7 @@ p, li { white-space: pre-wrap; }
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
<number>8</number>
</property>
<property name="value">
<number>2</number>
@ -312,8 +312,8 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="game_status_checkBox">
<property name="geometry">
<rect>
<x>1000</x>
<y>590</y>
<x>810</x>
<y>790</y>
<width>191</width>
<height>16</height>
</rect>
@ -339,8 +339,8 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="voiceovers_checkBox">
<property name="geometry">
<rect>
<x>1000</x>
<y>650</y>
<x>810</x>
<y>820</y>
<width>191</width>
<height>16</height>
</rect>
@ -363,8 +363,8 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="logistics_crates_checkBox">
<property name="geometry">
<rect>
<x>970</x>
<y>390</y>
<x>920</x>
<y>320</y>
<width>251</width>
<height>31</height>
</rect>
@ -375,17 +375,20 @@ p, li { white-space: pre-wrap; }
</font>
</property>
<property name="statusTip">
<string>Enable CTLD logistics crates for building ground units and air defenses.</string>
<string>Enable CTLD logistics crates for building ground units and air defenses. Pickup logistics containers to create new logistics sites.</string>
</property>
<property name="text">
<string>Logistics Crates</string>
<string>Logistics</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QCheckBox" name="awacs_checkBox">
<property name="geometry">
<rect>
<x>970</x>
<y>420</y>
<x>920</x>
<y>350</y>
<width>251</width>
<height>31</height>
</rect>
@ -408,8 +411,8 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="tankers_checkBox">
<property name="geometry">
<rect>
<x>970</x>
<y>450</y>
<x>920</x>
<y>380</y>
<width>251</width>
<height>31</height>
</rect>
@ -429,7 +432,7 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="apcs_spawn_checkBox">
<property name="geometry">
<rect>
<x>500</x>
<x>470</x>
<y>400</y>
<width>251</width>
<height>31</height>
@ -450,7 +453,7 @@ p, li { white-space: pre-wrap; }
<widget class="QSpinBox" name="inf_spawn_spinBox">
<property name="geometry">
<rect>
<x>710</x>
<x>680</x>
<y>360</y>
<width>71</width>
<height>31</height>
@ -468,36 +471,12 @@ p, li { white-space: pre-wrap; }
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
<number>20</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
<widget class="QCheckBox" name="smoke_checkBox">
<property name="geometry">
<rect>
<x>1000</x>
<y>620</y>
<width>191</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Not yet implemented.</string>
</property>
<property name="text">
<string>Smoke Active Zone</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="scenario_label_5">
<property name="geometry">
<rect>
@ -538,7 +517,7 @@ p, li { white-space: pre-wrap; }
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>500</x>
<x>470</x>
<y>360</y>
<width>191</width>
<height>31</height>
@ -557,7 +536,7 @@ p, li { white-space: pre-wrap; }
<property name="geometry">
<rect>
<x>870</x>
<y>700</y>
<y>640</y>
<width>291</width>
<height>31</height>
</rect>
@ -570,7 +549,7 @@ p, li { white-space: pre-wrap; }
<property name="geometry">
<rect>
<x>750</x>
<y>700</y>
<y>640</y>
<width>111</width>
<height>31</height>
</rect>
@ -587,7 +566,7 @@ p, li { white-space: pre-wrap; }
<widget class="QLabel" name="scenario_label_6">
<property name="geometry">
<rect>
<x>500</x>
<x>470</x>
<y>320</y>
<width>141</width>
<height>31</height>
@ -605,8 +584,8 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="force_offroad_checkBox">
<property name="geometry">
<rect>
<x>1000</x>
<y>560</y>
<x>810</x>
<y>760</y>
<width>191</width>
<height>16</height>
</rect>
@ -668,7 +647,7 @@ p, li { white-space: pre-wrap; }
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
<number>8</number>
</property>
<property name="value">
<number>2</number>
@ -679,7 +658,7 @@ p, li { white-space: pre-wrap; }
<rect>
<x>140</x>
<y>330</y>
<width>231</width>
<width>211</width>
<height>31</height>
</rect>
</property>
@ -697,7 +676,7 @@ p, li { white-space: pre-wrap; }
<rect>
<x>140</x>
<y>370</y>
<width>231</width>
<width>201</width>
<height>31</height>
</rect>
</property>
@ -731,7 +710,7 @@ p, li { white-space: pre-wrap; }
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
<number>8</number>
</property>
<property name="value">
<number>1</number>
@ -740,8 +719,8 @@ p, li { white-space: pre-wrap; }
<widget class="QCheckBox" name="zone_sams_checkBox">
<property name="geometry">
<rect>
<x>970</x>
<y>480</y>
<x>920</x>
<y>410</y>
<width>201</width>
<height>31</height>
</rect>
@ -758,6 +737,139 @@ p, li { white-space: pre-wrap; }
<string>Inactive Zone SAMs</string>
</property>
</widget>
<widget class="QLabel" name="scenario_label_9">
<property name="geometry">
<rect>
<x>740</x>
<y>490</y>
<width>171</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Zone FARP Conditions:</string>
</property>
</widget>
<widget class="QCheckBox" name="inf_spawn_voiceovers_checkBox">
<property name="geometry">
<rect>
<x>470</x>
<y>430</y>
<width>251</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="statusTip">
<string>Friendly/enemy APCs will drop infantry when reaching a new conflict zone.</string>
</property>
<property name="text">
<string>Voiceovers on Infantry Spawn</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QRadioButton" name="farp_never">
<property name="geometry">
<rect>
<x>950</x>
<y>500</y>
<width>95</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Never spawn FARPs in defeated conflict zones.</string>
</property>
<property name="text">
<string>Never</string>
</property>
<attribute name="buttonGroup">
<string notr="true">farp_buttonGroup</string>
</attribute>
</widget>
<widget class="QRadioButton" name="farp_gunits">
<property name="geometry">
<rect>
<x>950</x>
<y>530</y>
<width>221</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Only spawn FARPs in defeated conflict zones if we have sufficient ground units remaining.</string>
</property>
<property name="text">
<string>20% Ground Units Remaining</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">farp_buttonGroup</string>
</attribute>
</widget>
<widget class="QRadioButton" name="farp_always">
<property name="geometry">
<rect>
<x>950</x>
<y>560</y>
<width>221</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="statusTip">
<string>Always spawn a FARP in defeated conflict zones.</string>
</property>
<property name="text">
<string>Always</string>
</property>
<attribute name="buttonGroup">
<string notr="true">farp_buttonGroup</string>
</attribute>
</widget>
<widget class="QLabel" name="version_label">
<property name="geometry">
<rect>
<x>920</x>
<y>840</y>
<width>241</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Version string</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<zorder>background_label</zorder>
<zorder>scenario_comboBox</zorder>
<zorder>scenario_label</zorder>
@ -779,7 +891,6 @@ p, li { white-space: pre-wrap; }
<zorder>tankers_checkBox</zorder>
<zorder>apcs_spawn_checkBox</zorder>
<zorder>inf_spawn_spinBox</zorder>
<zorder>smoke_checkBox</zorder>
<zorder>scenario_label_5</zorder>
<zorder>forces_hint_label_2</zorder>
<zorder>label</zorder>
@ -793,6 +904,12 @@ p, li { white-space: pre-wrap; }
<zorder>scenario_label_8</zorder>
<zorder>e_attack_planes_spinBox</zorder>
<zorder>zone_sams_checkBox</zorder>
<zorder>scenario_label_9</zorder>
<zorder>inf_spawn_voiceovers_checkBox</zorder>
<zorder>farp_never</zorder>
<zorder>farp_gunits</zorder>
<zorder>farp_always</zorder>
<zorder>version_label</zorder>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
@ -865,4 +982,7 @@ p, li { white-space: pre-wrap; }
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="farp_buttonGroup"/>
</buttongroups>
</ui>

116
Generator/RotorOpsGroups.py Normal file
View File

@ -0,0 +1,116 @@
from dcs.countries import Russia, USA
import dcs.unit as unit
from dcs.mission import Mission
import dcs.mapping as mapping
import dcs.ships
import dcs.vehicles
import dcs.statics
import dcs.unit
import random
class VehicleTemplate:
class USA:
@staticmethod
def invisible_farp(mission, country, position, heading, name, late_activation):
farp = mission.farp(country, name, position, hidden=False, dead=False, farp_type=dcs.unit.InvisibleFARP)
vg = mission.vehicle_group_platoon(
country,
name,
[
dcs.vehicles.Unarmed.M_818,
dcs.vehicles.AirDefence.Vulcan,
dcs.vehicles.Unarmed.Ural_375
],
position.point_from_heading(45, 7),
heading=random.randint(0, 359),
formation=dcs.unitgroup.VehicleGroup.Formation.Star,
)
vg.late_activation = late_activation
return vg
@staticmethod
def logistics_site(mission, country, position, heading, prefix=""):
farp = mission.farp(country, "Logistics FARP", position, hidden=False, dead=False, farp_type=dcs.unit.InvisibleFARP)
sg = mission.static_group(
country,
prefix + " Logistics",
dcs.statics.Fortification.TV_tower,
position.point_from_heading(heading, 80),
heading
)
dist_from_center = 30
for i in range(1,4):
u = mission.static("logistic" + str(i), dcs.statics.Cargo.Iso_container_small)
u.position = position.point_from_heading(heading + 90, dist_from_center + (i * 15))
u.heading = 10
sg.add_unit(u)
for i in range(5,8):
u = mission.static("logistic" + str(i), dcs.statics.Cargo.Iso_container_small)
u.position = position.point_from_heading(heading + 270, dist_from_center + (i * 15))
u.heading = 10
sg.add_unit(u)
a_pos = position.point_from_heading(heading + 180, dist_from_center)
u = mission.static("Ammo Dump", dcs.statics.Fortification.FARP_Ammo_Dump_Coating)
u.position = a_pos.point_from_heading(heading + 90, 1)
u.heading = heading
sg.add_unit(u)
u = mission.static("FARP Tent", dcs.statics.Fortification.FARP_Tent)
u.position = a_pos.point_from_heading(heading + 90, dist_from_center + 20)
u.heading = heading
sg.add_unit(u)
u = mission.static("Fuel Depot", dcs.statics.Fortification.FARP_Fuel_Depot)
u.position = a_pos.point_from_heading(heading + 90, dist_from_center + 40)
u.heading = heading
sg.add_unit(u)
return sg
@staticmethod
def sa6_site(mission, country, position, heading, prefix="", skill=unit.Skill.Average):
vg = mission.vehicle_group(
country,
prefix + "SA6 site",
dcs.vehicles.AirDefence.Kub_1S91_str,
position,
heading
)
u = mission.vehicle("Launcher 1", dcs.vehicles.AirDefence.Kub_2P25_ln)
u.position = position.point_from_heading(heading + 140, 30)
u.heading = heading
vg.add_unit(u)
u = mission.vehicle("Launcher 2", dcs.vehicles.AirDefence.Kub_2P25_ln)
u.position = position.point_from_heading(heading + 210, 30)
u.heading = heading
vg.add_unit(u)
u = mission.vehicle("Rearm Truck", dcs.vehicles.Unarmed.Ural_375)
u.position = position.point_from_heading(heading + 0, 40)
u.heading = heading
vg.add_unit(u)
for u in vg.units:
u.skill = skill
return vg

View File

@ -3,6 +3,8 @@ from tokenize import String
import dcs
import os
import random
import RotorOpsGroups
import RotorOpsUnits
import time
@ -119,9 +121,6 @@ class RotorOpsMission:
elif zone.name.rfind("SPAWN") >= 0:
self.addZone(self.spawn_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
#add files and triggers necessary for RotorOps.lua script
self.addResources(self.sound_directory, self.script_directory)
self.scriptTriggerSetup(options)
blue_zones = self.staging_zones
red_zones = self.conflict_zones
@ -137,16 +136,21 @@ class RotorOpsMission:
self.m.terrain.airports[airport_name].set_blue()
#Add red ground units
#Populate Red zones with ground units
for zone_name in red_zones:
if red_forces:
self.addGroundGroups(red_zones[zone_name], self.m.country('Russia'), red_forces, options["red_quantity"])
if options["zone_protect_sams"]:
for zone_name in red_zones:
#Add blue FARPS
if options["zone_farps"] != "farp_never" and not options["defending"]:
RotorOpsGroups.VehicleTemplate.USA.invisible_farp(self.m, self.m.country('USA'),
red_zones[zone_name].position,
180, zone_name + " FARP", late_activation=True)
if options["zone_protect_sams"]:
self.m.vehicle_group(
self.m.country('Russia'),
zone_name + " Protection SAM NOAI",
"Static " + zone_name + " Protection SAM",
random.choice(RotorOpsUnits.e_zone_sams),
red_zones[zone_name].position,
heading=random.randint(0, 359),
@ -154,17 +158,28 @@ class RotorOpsMission:
formation=dcs.unitgroup.VehicleGroup.Formation.Star
)
#Add blue ground units
#Populate Blue zones with ground units
for zone_name in blue_zones:
if blue_forces:
self.addGroundGroups(blue_zones[zone_name], self.m.country('USA'), blue_forces,
options["blue_quantity"])
if options["zone_protect_sams"]:
for zone_name in blue_zones:
self.m.vehicle_group(
#add logistics sites
if options["crates"] and zone_name in self.staging_zones:
RotorOpsGroups.VehicleTemplate.USA.logistics_site(self.m, self.m.country('USA'),
blue_zones[zone_name].position,
180, zone_name)
if options["zone_protect_sams"] and options["defending"]:
vg = self.m.vehicle_group(
self.m.country('USA'),
zone_name + " Protection SAM NOAI",
"Static " + zone_name + " Protection SAM",
random.choice(RotorOpsUnits.e_zone_sams),
blue_zones[zone_name].position,
heading=random.randint(0, 359),
@ -172,6 +187,8 @@ class RotorOpsMission:
formation=dcs.unitgroup.VehicleGroup.Formation.Star
)
#Add player slots
if options["slots"] == "Multiple Slots":
self.addMultiplayerHelos()
@ -187,6 +204,10 @@ class RotorOpsMission:
self.m.map.position = self.m.terrain.airports[self.getCoalitionAirports("blue")[0]].position
self.m.map.zoom = 100000
#add files and triggers necessary for RotorOps.lua script
self.addResources(self.sound_directory, self.script_directory)
self.scriptTriggerSetup(options)
#Save the mission file
print(self.m.triggers.zones())
os.chdir(self.output_dir)
@ -414,7 +435,7 @@ class RotorOpsMission:
helo,
airport=enemy_airport,
maintask=dcs.task.CAS,
start_type=dcs.mission.StartType.Warm,
start_type=dcs.mission.StartType.Cold,
group_size=2)
zone_attack(afg, helo)
@ -424,7 +445,7 @@ class RotorOpsMission:
russia, "Enemy Attack Planes", plane["type"],
airport=enemy_airport,
maintask=dcs.task.CAS,
start_type=dcs.mission.StartType.Warm,
start_type=dcs.mission.StartType.Cold,
group_size=2)
zone_attack(afg, plane)
@ -450,6 +471,7 @@ class RotorOpsMission:
"RotorOps.force_offroad = " + lb("force_offroad") + "\n\n" +
"RotorOps.voice_overs = " + lb("voiceovers") + "\n\n" +
"RotorOps.zone_status_display = " + lb("game_display") + "\n\n" +
"RotorOps.inf_spawn_messages = " + lb("inf_spawn_msgs") + "\n\n" +
"RotorOps.inf_spawns_per_zone = " + lb("inf_spawn_qty") + "\n\n" +
"RotorOps.apcs_spawn_infantry = " + lb("apc_spawns_inf") + " \n\n"))))
self.m.triggerrules.triggers.append(trig)
@ -473,23 +495,50 @@ class RotorOpsMission:
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.startConflict(100)")))
self.m.triggerrules.triggers.append(trig)
#Add all zone-based triggers
#Add generic zone-based triggers
for index, zone_name in enumerate(self.conflict_zones):
z_active_trig = dcs.triggers.TriggerOnce(comment= zone_name + " Active")
z_active_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1))
z_active_trig.actions.append(dcs.action.DoScript(dcs.action.String("--Add any action you want here!")))
#Smoke action not working
# if options["smoke_zone"]:
# z_active_trig.actions.append(dcs.action.Smoke(zone=zone_name, density=1, preset=1))
# if index > 0:
# previous_zone = list(self.conflict_zones)[index - 1]
# z_active_trig.actions.append(dcs.action.Smoke(zone=previous_zone, density=1, preset=0))
#Zone protection SAMs
if options["zone_protect_sams"]:
z_active_trig.actions.append(dcs.action.DoScript(dcs.action.String("Group.destroy(Group.getByName('" + zone_name + " Protection SAM'))")))
self.m.triggerrules.triggers.append(z_active_trig)
#Zone protection SAMs
if options["zone_protect_sams"]:
for index, zone_name in enumerate(self.conflict_zones):
z_sams_trig = dcs.triggers.TriggerOnce(comment="Deactivate " + zone_name + " SAMs")
z_sams_trig.actions.append(dcs.action.DoScript(dcs.action.String("Group.destroy(Group.getByName('" + zone_name + " Protection SAM'))")))
self.m.triggerrules.triggers.append(z_sams_trig)
#Zone FARPS always
if options["zone_farps"] == "farp_always" and not options["defending"] and index > 0:
for index, zone_name in enumerate(self.conflict_zones):
if index > 0:
previous_zone = list(self.conflict_zones)[index - 1]
if not self.m.country("USA").find_group(previous_zone + " FARP"):
continue
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.actions.append(dcs.action.ActivateGroup(self.m.country("USA").find_group(previous_zone + " FARP").id))
self.m.triggerrules.triggers.append(z_farps_trig)
#Zone FARPS conditional on staged units remaining
if options["zone_farps"] == "farp_gunits":
for index, zone_name in enumerate(self.conflict_zones):
if index > 0:
previous_zone = list(self.conflict_zones)[index - 1]
if not self.m.country("USA").find_group(previous_zone + " FARP"):
continue
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.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.ActivateGroup(self.m.country("USA").find_group(previous_zone + " FARP").id))
self.m.triggerrules.triggers.append(z_farps_trig)
#Add attack helos triggers
for index in range(options["e_attack_helos"]):
random_zone_obj = random.choice(list(self.conflict_zones.items()))

View File

@ -21,6 +21,9 @@ Optional:
Tips:
-Position the center of conflict zones over an open area, as this position may be used to spawn units.
-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 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.
-The conflict game type can be played with blue forces on defense. In this mode the last conflict zone is the only troop pickup zone.
-Design your template so that it can be played in normal 'attacking' mode or 'defending' the conflict zone from enemy ground units starting from the staging zone.
-Keep the zones fairly close together, both for helicopter and ground unit travel times.

Binary file not shown.

View File

@ -1,28 +1,65 @@
# What is RotorOps?
RotorOps brings the ground war in DCS to life. Infantry becomes useful, and the helicopter operations that support them will directly contribute to the success of the mission.
RotorOps is a DCS script that makes it easy to create fun and engaging missions on the fly, directly in the mission editor and without ever opening a script file.
RotorOps is a mission generator and gameplay script for DCS: World. At its heart is a game type called Conflict, which requires helicopter operations to win battles on the ground. This is a fun territory capture game that promotes focus on individual 'conflict zones'.
At the core of the RotorOps script are AI enhancements that provide a dynamic ground war by causing automatic conflicts between ground forces and a progression of the front line.
![alt text](https://raw.githubusercontent.com/spencershepard/RotorOps/develop/documentation/images/rotorops%ss%200_3.png?raw=true)
# Key Features:
- Unique helicopter-focused gameplay
- Mission Generator windows app
- Over 100 built-in voiceovers (or for use in mission customization via trigger actions).
- Splash Damage 2 script for more realistic explosions that no longer require direct hits.
- CTLD troop and logistics transport automatically integrated and enhanced with sound effects.
- Play the role of the attacking or defending force.
- Single-player and multiplayer slot creation.
## Demo Missions
RotorOps: Aleppo Under Siege https://www.digitalcombatsimulator.com/en/files/3320079/
Rota Landing (Mr. Nobody) https://www.digitalcombatsimulator.com/en/files/3320186/
# RotorOps: Conflict
At the heart of this first release is a game type called Conflict, where attacking forces must clear Conflict Zones of defending ground forces. Once a zone is cleared, the next zone is activated and ground vehicles will move to the next Conflict Zone automatically. It's up to the rotorheads to pickup troops from the cleared zones and transport them to the active Conflict Zone.
Conflict is a game type in which attacking forces must clear Conflict Zones of defending ground forces. Once a zone is cleared, the next zone is activated and ground vehicles will move to the next Conflict Zone automatically.
![alt text](https://raw.githubusercontent.com/spencershepard/RotorOps/develop/documentation/images/rotorops%20conflict%20zones.png?raw=true)
## Dynamic Roles
A RotorOps conflict mission has opportunities for a variety of roles and tasks. There's no need to artificially select these roles, as the mission is fully dynamic.
### Do I have to transport troops?
This is really up to the mission designer. Transporting troops is not required for mission success in Conflict. However, friendly troops can be a very valuable asset, especially for clearing enemies in dense urban areas. If you're in a fixed wing or attack helicopter role, troop transport could be provided by other players or AI.
### Transport Logistics:
CTLD logistics crates are available from your starting base or staging zone. The logistics area has several logistics containers, that can themselves be moved to a new area via DCS inbuilt sling loading. If you can get one of these containers to a new area safely, it becomes a CTLD logistics zone for spawning crates to build ground units and air defenses.
### What about attack helicopters?
The constantly moving infantry is easier to see than the statues we are used to seeing. Destroying defending enemy vehicles so that our troops and vehicles can survive, and intercepting enemy reinforcements may be crucial to mission success.
### CAS:
The attacking side starts with ground units that move progressively through enemy conflict zones, seeking out enemy units within each zone. Protecting these ground forces is essential for establishing forward bases for rearming, troop pickup, and winning the battle
### How do I create a Conflict mission?
Just open a demo mission in the DCS mission editor and drop units into the Conflict Zones. These are trigger areas drawn in the mission editor that will automatically control the ground forces that enter them. This means that you do not need to worry about creating waypoints; enemy vehicles and infantry will seek each other out automatically. Move the Conflict Zones or change their size, add friendly or enemy units (remember, no waypoints needed).
### Troop Transport:
Never before has infantry been so important in DCS! Pick up troops from the staging area or a cleared conflict zone and deliver them to the active conflict zone. They will move through the zone until no enemies remain (or until they are killed). Very useful for flushing out enemy infantry and vehicles in densely forested or urban areas. JTAC units can mark important targets with smoke and laser.
Optional USER FLAGS are available to trigger events based on the status of individual zones and the game as a whole. Simple DO SCRIPT waypoint actions are available to drop troops from friendly or enemy AI helicopters or ground vehicles.
### Ground Attack:
Destroy enemy vehicles and infantry to ensure the survival of our own ground units. Clearing conflict zones of enemy units is essential for establishing forward bases for rearming and refueling. All enemy ground units must be destroyed to win the battle!
## RotorOps Mission Creator Guide: https://github.com/spencershepard/RotorOps/wiki/RotorOps:-Mission-Creator-Guide
### Fixed-wing CAP/CAS:
Enemy attack helicopters and planes are optional in the generator. Add slots in the mission generator for fixed-wing flights to provide cover for our helicopters and ground units. For a unique challenge, try Defense mode in a fixed-wing ground attack role...enemies are nearly always in motion.
## Mission Generator and Customization
The mission generator works by automatically placing units and trigger actions into a map template with defined airports and trigger zones.
Missions produced by the generator are easy to modify and understand. For example, units can be moved, and player flights can be added without issue. Use the result of the mission generator for quick plays, or build on it for something epic. Trigger actions are set up, labeled, and commented so that you can understand how things work and add your own actions. An additional library of voiceover files is provided for your own use.
Easily add your own templates for friendly/enemy ground units directly in the DCS mission editor.
Create your own scenarios for the RotorOps mission generator, using the DCS mission editor.
### RotorOps Mission Creator Guide:
For more detailed information on how the script works, see this wiki:
https://github.com/spencershepard/RotorOps/wiki/RotorOps:-Mission-Creator-Guide
***
@ -35,7 +72,7 @@ https://discord.gg/HFqjrZV9xD
https://www.patreon.com/spencershepard
### Developers
We welcome contributors to this new project! Please get in touch on Discord with new ideas or pickup/create an issue in this repo.
We welcome contributors to this new project! Please get in touch on Discord with new ideas or pickup/create an issue in this repo.
***
@ -44,3 +81,7 @@ RotorOps uses MIST and integrates CTLD:
https://github.com/mrSkortch/MissionScriptingTools
https://github.com/ciribob/DCS-CTLD
The mission generator would not be possible without PyDCS:
https://github.com/pydcs/dcs

View File

@ -1,6 +1,6 @@
RotorOps = {}
RotorOps.version = "1.2.4"
local debug = false
RotorOps.version = "1.2.5"
local debug = true
---[[ROTOROPS OPTIONS]]---
@ -20,13 +20,14 @@ RotorOps.inf_spawns_avail = 0 --this is the number of infantry group spawn event
RotorOps.inf_spawn_chance = 25 -- 0-100 the chance of spawning infantry in an active zone spawn zone, per 'assessUnitsInZone' loop (10 seconds)
RotorOps.inf_spawn_trigger_percent = 70 --infantry has a chance of spawning if the percentage of defenders remaining in zone is less than this value
RotorOps.inf_spawns_per_zone = 3 --number of infantry groups to spawn per zone
RotorOps.inf_spawn_messages = true --voiceovers and messages for infantry spawns
--RotorOps settings that are safe to change only before calling setupConflict()
RotorOps.transports = {'UH-1H', 'Mi-8MT', 'Mi-24P', 'SA342M', 'SA342L', 'SA342Mistral', 'UH-60L'} --players flying these will have ctld transport access
RotorOps.CTLD_crates = false
RotorOps.CTLD_sound_effects = true --sound effects for troop pickup/dropoffs
RotorOps.exclude_ai_group_name = "noai" --include this somewhere in a group name to exclude the group from being tasked in the active zone
RotorOps.exclude_ai_group_name = "Static" --include this somewhere in a group name to exclude the group from being tasked in the active zone
---[[END OF OPTIONS]]---
@ -49,6 +50,7 @@ RotorOps.ai_defending_vehicle_groups = {}
RotorOps.ai_attacking_vehicle_groups = {}
RotorOps.ai_tasks = {}
RotorOps.defending = false
RotorOps.staged_units_flag = 111
trigger.action.outText("ROTOR OPS STARTED: "..RotorOps.version, 5)
env.info("ROTOR OPS STARTED: "..RotorOps.version)
@ -58,10 +60,14 @@ RotorOps.eventHandler = {}
local commandDB = {}
local game_message_buffer = {}
local active_zone_initial_defenders
local initial_stage_units
local apcs = {} --table to keep track of infantry vehicles
local low_units_message_fired = false
local inf_spawn_zones = {}
local cooldown = {
["attack_helo_msg"] = 0,
["attack_plane_msg"] = 0,
}
RotorOps.gameMsgs = {
@ -144,6 +150,20 @@ RotorOps.gameMsgs = {
attack_planes = {
{'ENEMY ATTACK PLANES INBOUND!', 'enemy_attack_planes.ogg'},
},
attack_helos_prep = {
{'ENEMY ATTACK HELICOPTERS PREPARING FOR TAKEOFF!', 'e_attack_helicopters_preparing.ogg'},
},
attack_planes_prep = {
{'ENEMY ATTACK PLANES PREPARING FOR TAKEOFF!', 'e_attack_planes_preparing.ogg'},
},
infantry_spawned = {
{'ENEMY CONTACTS IN THE OPEN!', 'e_infantry_spawn1.ogg'},
{'ENEMY TROOPS LEAVING COVER!', 'e_infantry_spawn2.ogg'},
{'VISUAL ON ENEMY INFANTRY!', 'e_infantry_spawn3.ogg'},
{'ENEMY CONTACTS IN THE ACTIVE!', 'e_infantry_spawn4.ogg'},
{'ENEMY TROOPS IN THE ACTIVE!', 'e_infantry_spawn5.ogg'},
{'VISUAL ON ENEMY TROOPS!', 'e_infantry_spawn6.ogg'},
},
}
@ -154,15 +174,53 @@ local sound_effects = {
["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',},
}
function RotorOps.getTime()
return timer.getAbsTime() - timer.getTime0() --time since mission started
end
function RotorOps.eventHandler:onEvent(event)
if (world.event.S_EVENT_ENGINE_STARTUP == event.id) then --play some sound files when a player starts engines
local initaitor = event.initiator:getGroup():getID()
if RotorOps.defending then
trigger.action.outSoundForGroup(initaitor , RotorOps.gameMsgs.enemy_pushing[RotorOps.active_zone_index + 1][2])
else
trigger.action.outSoundForGroup(initaitor , RotorOps.gameMsgs.push[RotorOps.active_zone_index + 1][2])
---ENGINE STARTUP EVENTS
if (world.event.S_EVENT_ENGINE_STARTUP == event.id) then --play some sound files when a player starts engines
local initiator = event.initiator:getGroup():getID()
if #event.initiator:getGroup():getUnits() == 1 then --if there are no other units in the player flight group (preventing duplicated messages for ai wingman flights)
if RotorOps.defending then
trigger.action.outSoundForGroup(initiator , RotorOps.gameMsgs.enemy_pushing[RotorOps.active_zone_index + 1][2])
else
trigger.action.outSoundForGroup(initiator , RotorOps.gameMsgs.push[RotorOps.active_zone_index + 1][2])
end
end
end
---TAKEOFF EVENTS
if (world.event.S_EVENT_TAKEOFF == event.id) then
local initiator_name = event.initiator:getGroup():getName()
if (initiator_name == "Enemy Attack Helicopters") then
--we use flights of two aircraft which triggers two events, but we only want to use one event so we use a cooldown timer
if ((RotorOps.getTime() - cooldown["attack_helo_msg"]) > 90) then
RotorOps.gameMsg(RotorOps.gameMsgs.attack_helos)
cooldown["attack_helo_msg"] = RotorOps.getTime()
else
env.warning("RotorOps attack helo message skipped")
end
end
if initiator_name == "Enemy Attack Planes" then
if ((RotorOps.getTime() - cooldown["attack_plane_msg"]) > 90) then
RotorOps.gameMsg(RotorOps.gameMsgs.attack_planes)
cooldown["attack_plane_msg"] = RotorOps.getTime()
else
env.warning("RotorOps attack plane message skipped")
end
end
end
end
@ -507,7 +565,12 @@ function RotorOps.chargeEnemy(vars)
if vars.zone then ---mist getUnitsInZones method
local units_in_zone = mist.getUnitsInZones(mist.makeUnitTable({'[red][vehicle]'}), {vars.zone}, "spherical")
local units_in_zone
if enemy_coal == 1 then
units_in_zone = mist.getUnitsInZones(mist.makeUnitTable({'[red][vehicle]'}), {vars.zone}, "spherical")
elseif enemy_coal == 2 then
units_in_zone = mist.getUnitsInZones(mist.makeUnitTable({'[blue][vehicle]'}), {vars.zone}, "spherical")
end
local closest_dist = 10000
local closest_unit
for index, unit in pairs(units_in_zone) do
@ -872,6 +935,19 @@ function RotorOps.assessUnitsInZone(var)
end
end
--update staged units remaining flag
local staged_units_remaining = {}
for index, unit in pairs(RotorOps.staged_units) do
if unit:isExist() then
staged_units_remaining[#staged_units_remaining + 1] = unit
end
end
local percent_staged_remain = 0
percent_staged_remain = math.floor((#staged_units_remaining / #RotorOps.staged_units) * 100)
trigger.action.setUserFlag(RotorOps.staged_units_flag, percent_staged_remain)
debugMsg("Staged units remaining: "..percent_staged_remain.."%")
--is the game finished?
if all_zones_clear then
if RotorOps.defending == true then
@ -956,8 +1032,9 @@ function RotorOps.assessUnitsInZone(var)
ctld.spawnGroupAtTrigger("blue", 5, zone, 1000)
else
ctld.spawnGroupAtTrigger("red", 5, zone, 1000)
RotorOps.gameMsg(RotorOps.gameMsgs.infantry_spawned, math.random(1, #RotorOps.gameMsgs.infantry_spawned))
end
RotorOps.inf_spawns_avail = RotorOps.inf_spawns_avail - 1
env.info("ROTOR OPS: Spawned infantry. "..RotorOps.inf_spawns_avail.." spawns remaining in "..zone)
end
@ -1126,6 +1203,11 @@ function RotorOps.setupCTLD()
ctld.numberOfTroops = 24 --max loading size
ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy
ctld.maximumMoveDistance = 0 -- max distance for troops to move from drop point if no enemy is nearby
ctld.maximumDistanceLogistic = 300
ctld.minimumHoverHeight = 5.0 -- Lowest allowable height for crate hover
ctld.maximumHoverHeight = 15.0 -- Highest allowable height for crate hover
ctld.maxDistanceFromCrate = 7 -- Maximum distance from from crate for hover
ctld.hoverTime = 5 -- Time to hold hover above a crate for loading in seconds
ctld.unitLoadLimits = {
-- Remove the -- below to turn on options
@ -1269,12 +1351,12 @@ end
function RotorOps.spawnAttackHelos()
RotorOps.triggerSpawn("Enemy Attack Helicopters", RotorOps.gameMsgs.attack_helos)
RotorOps.triggerSpawn("Enemy Attack Helicopters", RotorOps.gameMsgs.attack_helos_prep)
end
function RotorOps.spawnAttackPlanes()
RotorOps.triggerSpawn("Enemy Attack Planes", RotorOps.gameMsgs.attack_planes)
RotorOps.triggerSpawn("Enemy Attack Planes", RotorOps.gameMsgs.attack_planes_prep)
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.