mirror of
https://github.com/spencershepard/RotorOps.git
synced 2025-11-10 15:45:30 +00:00
commit
02f496a406
BIN
Generator/Forces/red/RED Armor, Infantry & Artillery (Med).miz
Normal file
BIN
Generator/Forces/red/RED Armor, Infantry & Artillery (Med).miz
Normal file
Binary file not shown.
Binary file not shown.
BIN
Generator/Forces/red/RED Trucks & Infantry (Easy).miz
Normal file
BIN
Generator/Forces/red/RED Trucks & Infantry (Easy).miz
Normal file
Binary file not shown.
@ -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()
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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
116
Generator/RotorOpsGroups.py
Normal 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
|
||||
@ -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()))
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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.
65
README.md
65
README.md
@ -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.
|
||||
|
||||

|
||||
|
||||
# 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.
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
110
RotorOps.lua
110
RotorOps.lua
@ -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
|
||||
|
||||
|
||||
|
||||
BIN
documentation/images/rotorops ss 0_3.PNG
Normal file
BIN
documentation/images/rotorops ss 0_3.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 MiB |
BIN
sound/embedded/e_attack_helicopters_preparing.ogg
Normal file
BIN
sound/embedded/e_attack_helicopters_preparing.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/e_attack_planes_preparing.ogg
Normal file
BIN
sound/embedded/e_attack_planes_preparing.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/e_infantry_spawn1.ogg
Normal file
BIN
sound/embedded/e_infantry_spawn1.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/e_infantry_spawn2.ogg
Normal file
BIN
sound/embedded/e_infantry_spawn2.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/e_infantry_spawn3.ogg
Normal file
BIN
sound/embedded/e_infantry_spawn3.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/e_infantry_spawn4.ogg
Normal file
BIN
sound/embedded/e_infantry_spawn4.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/e_infantry_spawn5.ogg
Normal file
BIN
sound/embedded/e_infantry_spawn5.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/e_infantry_spawn6.ogg
Normal file
BIN
sound/embedded/e_infantry_spawn6.ogg
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user