mirror of
https://github.com/spencershepard/RotorOps.git
synced 2025-11-10 15:45:30 +00:00
commit
2f6c89983a
9
.gitignore
vendored
9
.gitignore
vendored
@ -15,5 +15,14 @@ Generator/utils/extract units/units.txt
|
||||
generator.log
|
||||
templates/Scenarios/user
|
||||
templates/Scenarios/downloaded
|
||||
templates/Imports/user
|
||||
templates/Imports/downloaded
|
||||
templates/Forces/user
|
||||
templates/Forces/downloaded
|
||||
config/user-data.yaml
|
||||
*.exe
|
||||
config/user-data.yaml
|
||||
server/user-files/modules/mapscript-sql.py
|
||||
distribution/
|
||||
MissionOutput/
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ import json
|
||||
import yaml
|
||||
import sys
|
||||
import os
|
||||
import operator
|
||||
|
||||
import RotorOpsMission as ROps
|
||||
import RotorOpsUnits
|
||||
@ -28,10 +29,18 @@ import qtmodern.windows
|
||||
|
||||
# UPDATE BUILD VERSION
|
||||
maj_version = 1
|
||||
minor_version = 1
|
||||
patch_version = 2
|
||||
minor_version = 2
|
||||
patch_version = 0
|
||||
|
||||
modules_version = 2
|
||||
modules_url = 'https://dcs-helicopters.com/user-files/modules/'
|
||||
version_url = 'https://dcs-helicopters.com/app-updates/versions.yaml'
|
||||
modules_map_url = 'https://dcs-helicopters.com/user-files/modules/module-map-v2.yaml'
|
||||
ratings_url = 'https://dcs-helicopters.com/user-files/ratings.php'
|
||||
allowed_paths = ['templates\\Scenarios\\downloaded', 'templates\\Forces\\downloaded', 'templates\\Imports\\downloaded']
|
||||
|
||||
user_files_url = 'https://dcs-helicopters.com/user-files/'
|
||||
version_url = 'https://dcs-helicopters.com/app-updates/versioncheck.yaml'
|
||||
|
||||
#Setup logfile and exception handler
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -50,27 +59,39 @@ class directories:
|
||||
os.chdir("..")
|
||||
cls.home_dir = os.getcwd()
|
||||
cls.scenarios = cls.home_dir + "\\templates\\Scenarios"
|
||||
cls.forces = cls.home_dir + "\\templates\\Forces"
|
||||
cls.forces_downloaded = cls.home_dir + "\\templates\\Forces\\downloaded"
|
||||
cls.forces_user = cls.home_dir + "\\templates\\Forces\\user"
|
||||
cls.scripts = cls.home_dir + "\\scripts"
|
||||
cls.sound = cls.home_dir + "\\sound\\embedded"
|
||||
cls.output = cls.home_dir + "\\MissionOutput"
|
||||
cls.assets = cls.home_dir + "\\assets"
|
||||
cls.imports = cls.home_dir + "\\templates\\Imports"
|
||||
cls.imports_downloaded = cls.home_dir + "\\templates\\Imports\\downloaded"
|
||||
cls.imports_user = cls.home_dir + "\\templates\\Imports\\user"
|
||||
cls.user_datafile_path = cls.home_dir + "\\config\\user-data.yaml"
|
||||
cls.scenarios_downloaded = cls.scenarios + "\\downloaded"
|
||||
cls.scenarios_user = cls.scenarios + "\\user"
|
||||
cls.default_config = cls.home_dir + '\\config\\default-config.yaml'
|
||||
os.chdir(current_dir)
|
||||
|
||||
directories.find()
|
||||
@classmethod
|
||||
def createDirectories(cls):
|
||||
required_dirs = [cls.scenarios_user, cls.scenarios_downloaded, cls.imports_user, cls.imports_downloaded, cls.forces_user, cls.forces_downloaded, cls.output]
|
||||
for path in required_dirs:
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
import MissionGeneratorScenario
|
||||
|
||||
directories.find()
|
||||
directories.createDirectories()
|
||||
|
||||
import MissionGeneratorTemplates
|
||||
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
if issubclass(exc_type, KeyboardInterrupt): #example of handling error subclasses
|
||||
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
||||
return
|
||||
|
||||
QApplication.restoreOverrideCursor()
|
||||
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
|
||||
msg = QMessageBox()
|
||||
msg.setWindowTitle("Uncaught exception")
|
||||
@ -82,9 +103,6 @@ sys.excepthook = handle_exception
|
||||
|
||||
|
||||
version_string = str(maj_version) + "." + str(minor_version) + "." + str(patch_version)
|
||||
# scenarios = []
|
||||
red_forces_files = []
|
||||
blue_forces_files = []
|
||||
defenders_text = "Defending Forces:"
|
||||
attackers_text = "Attacking Forces:"
|
||||
ratings_json = None
|
||||
@ -120,16 +138,17 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.player_slots = []
|
||||
self.user_output_dir = None
|
||||
self.user_data = None
|
||||
self.forces_list = []
|
||||
self.imports_list = []
|
||||
|
||||
self.user_data = self.loadUserData()
|
||||
|
||||
self.m = ROps.RotorOpsMission()
|
||||
self.setupUi(self)
|
||||
self.connectSignalsSlots()
|
||||
self.populateScenarios()
|
||||
self.populateForces("red", self.redforces_comboBox, red_forces_files)
|
||||
self.populateForces("blue", self.blueforces_comboBox, blue_forces_files)
|
||||
self.populateForces()
|
||||
self.populateSlotSelection()
|
||||
self.getImports()
|
||||
|
||||
# self.blue_forces_label.setText(attackers_text)
|
||||
# self.red_forces_label.setText(defenders_text)
|
||||
@ -139,11 +158,15 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
"QStatusBar{padding-left:5px;}")
|
||||
self.version_label.setText("Version " + version_string)
|
||||
|
||||
self.scenarioChanged()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self.time_comboBox.addItem("Default Time")
|
||||
self.time_comboBox.addItem("Day")
|
||||
self.time_comboBox.addItem("Night")
|
||||
self.time_comboBox.addItem("Dusk")
|
||||
self.time_comboBox.addItem("Dawn")
|
||||
self.time_comboBox.addItem("Noon")
|
||||
self.time_comboBox.addItem("Random")
|
||||
|
||||
|
||||
def connectSignalsSlots(self):
|
||||
@ -212,7 +235,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
basename = filename.removesuffix('.miz')
|
||||
mizpath = os.path.join(path, folder, filename)
|
||||
# create scenario object
|
||||
s = MissionGeneratorScenario.Scenario(mizpath, basename)
|
||||
s = MissionGeneratorTemplates.Scenario(mizpath, basename)
|
||||
|
||||
#apply some properties if found in the downloads directory
|
||||
if path == directories.scenarios_downloaded:
|
||||
@ -221,7 +244,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
s.packageID = folder
|
||||
|
||||
if ratings_json:
|
||||
print(ratings_json)
|
||||
for module in ratings_json:
|
||||
if module['package'] == folder:
|
||||
s.rating = module["avg_rating"]
|
||||
@ -256,8 +278,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
t_scenarios.append(s)
|
||||
scenarios = t_scenarios.copy()
|
||||
|
||||
#self.scenario_comboBox.addItem(s.name)
|
||||
self.scenarios_list = scenarios.copy()
|
||||
self.scenarios_list = sorted(scenarios, key=lambda x: x.name, reverse=False)
|
||||
|
||||
for s in self.scenarios_list:
|
||||
self.scenario_comboBox.addItem(s.name)
|
||||
|
||||
@ -268,18 +290,67 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.populateScenarios()
|
||||
# self.scenarioChanged() haven't tried yet
|
||||
|
||||
def populateForces(self, side, combobox, files_list):
|
||||
os.chdir(directories.home_dir)
|
||||
# os.chdir(directories.forces + "/" + side)
|
||||
os.chdir(directories.forces)
|
||||
path = os.getcwd()
|
||||
dir_list = os.listdir(path)
|
||||
logger.info("Looking for " + side + " Forces files in '" + path)
|
||||
def populateForces(self):
|
||||
self.forces_list = []
|
||||
|
||||
for filename in dir_list:
|
||||
for path in [directories.forces_downloaded, directories.forces_user]:
|
||||
logger.info("Looking for forces files in " + path)
|
||||
os.chdir(path)
|
||||
module_folders = next(os.walk('.'))[1]
|
||||
|
||||
for folder in module_folders:
|
||||
for filename in os.listdir(folder):
|
||||
if filename.endswith(".miz"):
|
||||
files_list.append(filename)
|
||||
combobox.addItem(filename.removesuffix('.miz'))
|
||||
basename = filename.removesuffix('.miz')
|
||||
mizpath = os.path.join(path, folder, filename)
|
||||
config_file_path = os.path.join(path, folder, basename + '.yaml')
|
||||
if os.path.exists(config_file_path):
|
||||
# create forces object with config
|
||||
try:
|
||||
config = yaml.safe_load(open(config_file_path))
|
||||
f = MissionGeneratorTemplates.Forces(mizpath, filename, config)
|
||||
self.forces_list.append(f)
|
||||
except:
|
||||
logger.error("Error in " + config_file_path)
|
||||
|
||||
else:
|
||||
# create forces object without config
|
||||
f = MissionGeneratorTemplates.Forces(mizpath, basename)
|
||||
self.forces_list.append(f)
|
||||
|
||||
self.forces_list = sorted(self.forces_list, key=lambda x: x.name, reverse=False)
|
||||
|
||||
for forces in self.forces_list:
|
||||
self.redforces_comboBox.addItem(forces.name)
|
||||
self.blueforces_comboBox.addItem(forces.name)
|
||||
|
||||
def getImports(self):
|
||||
self.imports_list = []
|
||||
|
||||
for path in [directories.imports_downloaded, directories.imports_user]:
|
||||
logger.info("Looking for imports files in " + path)
|
||||
os.chdir(path)
|
||||
module_folders = next(os.walk('.'))[1]
|
||||
|
||||
for folder in module_folders:
|
||||
for filename in os.listdir(folder):
|
||||
if filename.endswith(".miz"):
|
||||
basename = filename.removesuffix('.miz')
|
||||
mizpath = os.path.join(path, folder, filename)
|
||||
config_file_path = os.path.join(path, folder, basename + '.yaml')
|
||||
if os.path.exists(config_file_path):
|
||||
# create imports object with config
|
||||
try:
|
||||
config = yaml.safe_load(config_file_path)
|
||||
f = MissionGeneratorTemplates.Import(mizpath, filename, config)
|
||||
self.imports_list.append(f)
|
||||
except:
|
||||
logger.error("Error in " + config_file_path)
|
||||
|
||||
else:
|
||||
# create imports object without config
|
||||
f = MissionGeneratorTemplates.Import(mizpath, filename)
|
||||
self.imports_list.append(f)
|
||||
|
||||
def populateSlotSelection(self):
|
||||
self.slot_template_comboBox.addItem("Multiple Slots")
|
||||
@ -321,7 +392,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
# reset some UI elements
|
||||
self.defense_checkBox.setEnabled(True)
|
||||
if self.lockedSlot():
|
||||
self.slot_template_comboBox.removeItem(self.lockedSlot())
|
||||
|
||||
self.slot_template_comboBox.setEnabled(True)
|
||||
@ -362,11 +432,14 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
button.setEnabled(True)
|
||||
|
||||
if 'blue_forces' in config:
|
||||
self.blueforces_comboBox.setCurrentIndex(self.blueforces_comboBox.findText(config['blue_forces']))
|
||||
for template in self.forces_list:
|
||||
if template.basename == config['blue_forces']:
|
||||
self.blueforces_comboBox.setCurrentIndex(self.blueforces_comboBox.findText(template.name))
|
||||
|
||||
if 'red_forces' in config:
|
||||
if self.redforces_comboBox.findText(config['red_forces']) >= 0:
|
||||
self.redforces_comboBox.setCurrentIndex(self.redforces_comboBox.findText(config['red_forces']))
|
||||
for template in self.forces_list:
|
||||
if template.basename == config['red_forces']:
|
||||
self.redforces_comboBox.setCurrentIndex(self.redforces_comboBox.findText(template.name))
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Error loading config file: " + str(e))
|
||||
@ -416,16 +489,16 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
return
|
||||
|
||||
QApplication.setOverrideCursor(Qt.WaitCursor)
|
||||
self.slot_template_comboBox.setCurrentIndex(0)
|
||||
|
||||
self.scenario = self.scenarios_list[self.scenario_comboBox.currentIndex()]
|
||||
|
||||
if self.scenario.config:
|
||||
self.applyScenarioConfig(self.scenario.config)
|
||||
self.m.setConfig(self.scenario.config)
|
||||
else:
|
||||
# reset generator options to default
|
||||
default_config = self.loadScenarioConfig(directories.default_config)
|
||||
self.applyScenarioConfig(default_config)
|
||||
self.m.setConfig(default_config)
|
||||
|
||||
if self.scenario.config:
|
||||
self.applyScenarioConfig(self.scenario.config)
|
||||
|
||||
path = self.scenario.path.removesuffix(".miz") + ".jpg"
|
||||
if os.path.isfile(path):
|
||||
@ -462,17 +535,27 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def generateMissionAction(self):
|
||||
QApplication.setOverrideCursor(Qt.WaitCursor)
|
||||
|
||||
red_forces_filename = red_forces_files[self.redforces_comboBox.currentIndex()]
|
||||
blue_forces_filename = blue_forces_files[self.blueforces_comboBox.currentIndex()]
|
||||
red_forces = self.forces_list[self.redforces_comboBox.currentIndex()]
|
||||
blue_forces = self.forces_list[self.blueforces_comboBox.currentIndex()]
|
||||
scenario_name = self.scenario.name
|
||||
scenario_path = self.scenario.path
|
||||
source = "offline"
|
||||
|
||||
credits = ("'" + scenario_name + "' mission template by " + self.scenario.author + "\n" +
|
||||
"'" + red_forces.name + "' by " + red_forces.author + "\n" +
|
||||
"'" + blue_forces.name + "' by " + blue_forces.author + "\n"
|
||||
)
|
||||
|
||||
objects = {
|
||||
"imports": self.imports_list,
|
||||
}
|
||||
|
||||
data = {
|
||||
"source": source,
|
||||
"objects": objects,
|
||||
"credits": credits,
|
||||
"scenario_file": scenario_path,
|
||||
"scenario_name": scenario_name,
|
||||
"red_forces_filename": red_forces_filename,
|
||||
"blue_forces_filename": blue_forces_filename,
|
||||
"red_forces_path": red_forces.path,
|
||||
"blue_forces_path": blue_forces.path,
|
||||
"red_quantity": self.redqty_spinBox.value(),
|
||||
"blue_quantity": self.blueqty_spinBox.value(),
|
||||
"inf_spawn_qty": self.inf_spawn_spinBox.value(),
|
||||
@ -483,7 +566,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
"f_awacs": self.awacs_checkBox.isChecked(),
|
||||
"f_tankers": self.tankers_checkBox.isChecked(),
|
||||
"voiceovers": self.voiceovers_checkBox.isChecked(),
|
||||
"force_offroad": self.force_offroad_checkBox.isChecked(),
|
||||
"force_offroad": self.scenario.getConfigValue("force_offroad", default=False),
|
||||
"game_display": self.game_status_checkBox.isChecked(),
|
||||
"defending": self.defense_checkBox.isChecked(),
|
||||
"slots": self.slot_template_comboBox.currentText(),
|
||||
@ -494,6 +577,17 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
"smoke_pickup_zones": self.smoke_pickup_zone_checkBox.isChecked(),
|
||||
"player_slots": self.player_slots,
|
||||
"player_hotstart": self.hotstart_checkBox.isChecked(),
|
||||
"random_weather": self.random_weather_checkBox.isChecked(),
|
||||
"time": self.time_comboBox.currentText(),
|
||||
"start_trigger": self.scenario.getConfigValue("start_trigger", default=True),
|
||||
"end_trigger": self.scenario.getConfigValue("end_trigger", default=True),
|
||||
"farp_spawns": self.farp_spawn_checkBox.isChecked(),
|
||||
"staging_logistics_file": self.scenario.getConfigValue("staging_logistics_file", default=None),
|
||||
"zone_farp_file": self.scenario.getConfigValue("zone_farp_file", default=None),
|
||||
"defensive_farp_file": self.scenario.getConfigValue("defensive_farp_file", default=None),
|
||||
"logistics_farp_file": self.scenario.getConfigValue("logistics_farp_file", default=None),
|
||||
"zone_protect_file": self.scenario.getConfigValue("zone_protect_file", default=None),
|
||||
"script": self.scenario.getConfigValue("script", default=None),
|
||||
}
|
||||
|
||||
logger.info("Generating mission with options:")
|
||||
@ -674,7 +768,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def checkVersion(splashscreen):
|
||||
|
||||
version_url = 'https://dcs-helicopters.com/app-updates/versioncheck.yaml'
|
||||
|
||||
try:
|
||||
r = requests.get(version_url, allow_redirects=False, timeout=7)
|
||||
v = yaml.safe_load(r.content)
|
||||
@ -693,31 +787,33 @@ def checkVersion(splashscreen):
|
||||
|
||||
|
||||
|
||||
modules_url = 'https://dcs-helicopters.com/user-files/modules/'
|
||||
version_url = 'https://dcs-helicopters.com/app-updates/versions.yaml'
|
||||
modules_map_url = 'https://dcs-helicopters.com/user-files/modules/module-map.yaml'
|
||||
ratings_url = 'https://dcs-helicopters.com/user-files/ratings.php'
|
||||
|
||||
def loadModules(splashscreen):
|
||||
msg = QMessageBox()
|
||||
msg.setWindowTitle("Unable to connect to server")
|
||||
msg.setText(
|
||||
"We were unable to connect to the RotorOps server to download content. This is a temporary problem, so please try again later. If the problem persists, please get in touch via Discord.")
|
||||
|
||||
try:
|
||||
r = requests.get(modules_map_url, allow_redirects=False, timeout=7)
|
||||
if not r.status_code == 200:
|
||||
logger.error("Could not retrieve the modules map.")
|
||||
x = msg.exec_()
|
||||
return
|
||||
except:
|
||||
logger.error("Failed to retrieve module map.")
|
||||
x = msg.exec_()
|
||||
return
|
||||
|
||||
module_list = yaml.safe_load(r.content)
|
||||
files_success = []
|
||||
files_failed = []
|
||||
new_scenarios = []
|
||||
updated_scenarios = []
|
||||
new_modules = []
|
||||
updated_modules = []
|
||||
outversioned_modules = []
|
||||
|
||||
|
||||
# Download scenarios files
|
||||
#os.chdir(directories.scenarios)
|
||||
|
||||
if module_list:
|
||||
|
||||
for module in module_list:
|
||||
@ -725,14 +821,30 @@ def loadModules(splashscreen):
|
||||
should_download = False
|
||||
new_module = False
|
||||
|
||||
# only allow predefined paths
|
||||
dp = module_list[module]["path"]
|
||||
if dp not in allowed_paths:
|
||||
logger.warning("Invalid path for module: " + module)
|
||||
continue
|
||||
|
||||
# check if local version already exists
|
||||
package_file_path = os.path.join(directories.scenarios_downloaded, module, "package.yaml")
|
||||
package_file_path = os.path.join(directories.home_dir, module_list[module]["path"], module, "package.yaml")
|
||||
|
||||
if os.path.exists(package_file_path):
|
||||
pkg_file = yaml.safe_load(open(package_file_path))
|
||||
else:
|
||||
pkg_file = None
|
||||
|
||||
# compare required generator version and actual version
|
||||
if 'requires' in module_list[module]:
|
||||
if module_list[module]['requires'] > modules_version:
|
||||
name = 'unknown module'
|
||||
if 'name' in module_list[module]:
|
||||
name = module_list[module]['name']
|
||||
outversioned_modules.append(name)
|
||||
continue
|
||||
|
||||
|
||||
# compare local and remote versions
|
||||
if pkg_file and 'version' in pkg_file:
|
||||
local_version = pkg_file['version']
|
||||
@ -744,6 +856,20 @@ def loadModules(splashscreen):
|
||||
should_download = True
|
||||
new_module = True
|
||||
|
||||
# delete modules with 'remove' dist property
|
||||
if 'dist' in module_list[module] and module_list[module]['dist'] == 'remove':
|
||||
for filename in module_list[module]["files"]:
|
||||
module_dir = os.path.join(directories.home_dir, module_list[module]["path"], module)
|
||||
file_path = os.path.join(module_dir, filename)
|
||||
if os.path.exists(file_path):
|
||||
try:
|
||||
os.remove(file_path)
|
||||
print("Removed module file: " + filename)
|
||||
except:
|
||||
logger.error("Error while trying to remove " + filename)
|
||||
continue
|
||||
|
||||
# download files
|
||||
if should_download:
|
||||
logger.info("Updating module: " + module)
|
||||
module_dir = os.path.join(directories.home_dir, module_list[module]["path"], module)
|
||||
@ -751,10 +877,11 @@ def loadModules(splashscreen):
|
||||
# download files in remote package
|
||||
for filename in module_list[module]["files"]:
|
||||
broken_file = False
|
||||
type_path = module_list[module]["type"]
|
||||
splash.showMessage("Downloading " + filename + " ...", Qt.AlignHCenter | Qt.AlignTop, Qt.white)
|
||||
app.processEvents()
|
||||
|
||||
url = modules_url + module + "/" + filename
|
||||
url = modules_url + type_path + "/" + module + "/" + filename
|
||||
try:
|
||||
r = requests.get(url, allow_redirects=False, timeout=10)
|
||||
except:
|
||||
@ -771,9 +898,9 @@ def loadModules(splashscreen):
|
||||
# do some stuff for the dialog popup
|
||||
if filename.endswith('.miz') and "name" in module_list[module]:
|
||||
if new_module:
|
||||
new_scenarios.append(module_list[module]["name"])
|
||||
new_modules.append(module_list[module]["name"])
|
||||
else:
|
||||
updated_scenarios.append(module_list[module]["name"])
|
||||
updated_modules.append(module_list[module]["name"])
|
||||
else:
|
||||
broken_file = True
|
||||
files_failed.append(filename)
|
||||
@ -791,7 +918,7 @@ def loadModules(splashscreen):
|
||||
logger.error("Problem encountered with modules map.")
|
||||
|
||||
# show a popup if we downloaded any packages
|
||||
if len(files_success) > 0 or len(files_failed) > 0:
|
||||
if len(files_success) > 0 or len(files_failed) > 0 or len(outversioned_modules) > 0:
|
||||
if len(files_failed) > 0:
|
||||
fs = ""
|
||||
for filename in files_failed:
|
||||
@ -800,16 +927,18 @@ def loadModules(splashscreen):
|
||||
msg = QMessageBox()
|
||||
msg.setWindowTitle("Downloaded Files")
|
||||
message = ""
|
||||
if len(new_scenarios) > 0:
|
||||
message = message + "New scenarios added: \n\n"
|
||||
for name in new_scenarios:
|
||||
if len(new_modules) > 0:
|
||||
message = message + "New modules added: \n\n"
|
||||
for name in new_modules:
|
||||
message = message + name + "\n"
|
||||
if len(updated_scenarios) > 0:
|
||||
message = message + "\nScenarios updated: \n"
|
||||
for name in updated_scenarios:
|
||||
if len(updated_modules) > 0:
|
||||
message = message + "\nModules updated: \n"
|
||||
for name in updated_modules:
|
||||
message = message + name + "\n"
|
||||
if len(files_failed) > 0:
|
||||
message = message + "\n\n" + str(len(files_failed)) + " files failed."
|
||||
if len(outversioned_modules) > 0:
|
||||
message = message + "\n\n" + str(len(outversioned_modules)) + " modules did not download because you need an required update."
|
||||
msg.setText(message)
|
||||
x = msg.exec_()
|
||||
else:
|
||||
|
||||
@ -19,6 +19,8 @@ class Scenario:
|
||||
self.rating_qty = None
|
||||
self.packageID = None
|
||||
self.local_rating = None
|
||||
self.author = "unknown"
|
||||
|
||||
|
||||
def applyConfig(self, config):
|
||||
self.config = config
|
||||
@ -31,8 +33,16 @@ class Scenario:
|
||||
if 'tags' in config:
|
||||
for tag in config['tags']:
|
||||
self.tags.append(tag)
|
||||
if 'author' in config:
|
||||
self.author = config["author"]
|
||||
|
||||
|
||||
def getConfigValue(self, key, default):
|
||||
if self.config and key in self.config:
|
||||
return self.config[key]
|
||||
else:
|
||||
return default
|
||||
|
||||
|
||||
def evaluateMiz(self):
|
||||
# check if we have the miz file
|
||||
@ -116,3 +126,35 @@ class Scenario:
|
||||
|
||||
|
||||
|
||||
class Forces:
|
||||
|
||||
def __init__(self, path, filename, config=None):
|
||||
self.path = path
|
||||
self.filename = filename
|
||||
self.basename = filename.removesuffix('.miz')
|
||||
self.name = filename.removesuffix('.miz')
|
||||
self.author = "unknown"
|
||||
|
||||
|
||||
if config:
|
||||
if 'name' in config:
|
||||
self.name = config["name"]
|
||||
|
||||
if 'author' in config:
|
||||
self.author = config["author"]
|
||||
|
||||
class Import:
|
||||
|
||||
def __init__(self, path, filename, config=None):
|
||||
self.path = path
|
||||
self.filename = filename
|
||||
self.name = filename.removesuffix('.miz')
|
||||
self.author = "unknown"
|
||||
|
||||
|
||||
if config:
|
||||
if 'name' in config:
|
||||
self.name = config["name"]
|
||||
|
||||
if 'author' in config:
|
||||
self.author = config["author"]
|
||||
@ -34,7 +34,7 @@ class Ui_MainWindow(object):
|
||||
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.logistics_crates_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.logistics_crates_checkBox.setGeometry(QtCore.QRect(990, 211, 251, 28))
|
||||
self.logistics_crates_checkBox.setGeometry(QtCore.QRect(980, 211, 251, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
@ -42,7 +42,7 @@ class Ui_MainWindow(object):
|
||||
self.logistics_crates_checkBox.setChecked(True)
|
||||
self.logistics_crates_checkBox.setObjectName("logistics_crates_checkBox")
|
||||
self.zone_sams_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.zone_sams_checkBox.setGeometry(QtCore.QRect(990, 320, 241, 28))
|
||||
self.zone_sams_checkBox.setGeometry(QtCore.QRect(980, 320, 241, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
@ -79,9 +79,9 @@ class Ui_MainWindow(object):
|
||||
self.description_textBrowser.setObjectName("description_textBrowser")
|
||||
self.defense_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.defense_checkBox.setEnabled(True)
|
||||
self.defense_checkBox.setGeometry(QtCore.QRect(470, 120, 156, 28))
|
||||
self.defense_checkBox.setGeometry(QtCore.QRect(470, 130, 156, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setPointSize(11)
|
||||
font.setBold(False)
|
||||
self.defense_checkBox.setFont(font)
|
||||
self.defense_checkBox.setCheckable(True)
|
||||
@ -116,7 +116,7 @@ class Ui_MainWindow(object):
|
||||
self.scenario_label_8.setFont(font)
|
||||
self.scenario_label_8.setObjectName("scenario_label_8")
|
||||
self.slot_template_comboBox = QtWidgets.QComboBox(self.centralwidget)
|
||||
self.slot_template_comboBox.setGeometry(QtCore.QRect(960, 384, 271, 33))
|
||||
self.slot_template_comboBox.setGeometry(QtCore.QRect(980, 474, 271, 33))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
@ -216,7 +216,7 @@ class Ui_MainWindow(object):
|
||||
self.e_attack_helos_spinBox.setKeyboardTracking(True)
|
||||
self.e_attack_helos_spinBox.setMinimum(0)
|
||||
self.e_attack_helos_spinBox.setMaximum(8)
|
||||
self.e_attack_helos_spinBox.setProperty("value", 2)
|
||||
self.e_attack_helos_spinBox.setProperty("value", 1)
|
||||
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(570, 180, 271, 24))
|
||||
@ -226,20 +226,20 @@ class Ui_MainWindow(object):
|
||||
self.scenario_label_7.setFont(font)
|
||||
self.scenario_label_7.setObjectName("scenario_label_7")
|
||||
self.label_2 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.label_2.setGeometry(QtCore.QRect(840, 390, 111, 24))
|
||||
self.label_2.setGeometry(QtCore.QRect(860, 480, 111, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.label_2.setFont(font)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.scenario_label_9 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.scenario_label_9.setGeometry(QtCore.QRect(490, 450, 251, 23))
|
||||
self.scenario_label_9.setGeometry(QtCore.QRect(480, 401, 251, 23))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
self.scenario_label_9.setFont(font)
|
||||
self.scenario_label_9.setObjectName("scenario_label_9")
|
||||
self.awacs_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.awacs_checkBox.setGeometry(QtCore.QRect(990, 246, 241, 28))
|
||||
self.awacs_checkBox.setGeometry(QtCore.QRect(980, 246, 241, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
@ -247,7 +247,7 @@ 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(990, 282, 241, 28))
|
||||
self.tankers_checkBox.setGeometry(QtCore.QRect(980, 282, 241, 28))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
@ -255,21 +255,21 @@ class Ui_MainWindow(object):
|
||||
self.tankers_checkBox.setChecked(True)
|
||||
self.tankers_checkBox.setObjectName("tankers_checkBox")
|
||||
self.voiceovers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.voiceovers_checkBox.setGeometry(QtCore.QRect(960, 517, 171, 24))
|
||||
self.voiceovers_checkBox.setGeometry(QtCore.QRect(500, 594, 171, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.voiceovers_checkBox.setFont(font)
|
||||
self.voiceovers_checkBox.setChecked(True)
|
||||
self.voiceovers_checkBox.setObjectName("voiceovers_checkBox")
|
||||
self.smoke_pickup_zone_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.smoke_pickup_zone_checkBox.setGeometry(QtCore.QRect(960, 460, 271, 24))
|
||||
self.smoke_pickup_zone_checkBox.setGeometry(QtCore.QRect(500, 541, 231, 20))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.smoke_pickup_zone_checkBox.setFont(font)
|
||||
self.smoke_pickup_zone_checkBox.setChecked(False)
|
||||
self.smoke_pickup_zone_checkBox.setObjectName("smoke_pickup_zone_checkBox")
|
||||
self.game_status_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.game_status_checkBox.setGeometry(QtCore.QRect(960, 490, 271, 24))
|
||||
self.game_status_checkBox.setGeometry(QtCore.QRect(500, 570, 221, 21))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.game_status_checkBox.setFont(font)
|
||||
@ -277,24 +277,24 @@ class Ui_MainWindow(object):
|
||||
self.game_status_checkBox.setTristate(False)
|
||||
self.game_status_checkBox.setObjectName("game_status_checkBox")
|
||||
self.label = QtWidgets.QLabel(self.centralwidget)
|
||||
self.label.setGeometry(QtCore.QRect(570, 380, 261, 23))
|
||||
self.label.setGeometry(QtCore.QRect(570, 340, 261, 23))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.label.setFont(font)
|
||||
self.label.setObjectName("label")
|
||||
self.inf_spawn_spinBox = QtWidgets.QSpinBox(self.centralwidget)
|
||||
self.inf_spawn_spinBox.setGeometry(QtCore.QRect(510, 380, 47, 31))
|
||||
self.inf_spawn_spinBox.setGeometry(QtCore.QRect(510, 340, 51, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(12)
|
||||
self.inf_spawn_spinBox.setFont(font)
|
||||
self.inf_spawn_spinBox.setButtonSymbols(QtWidgets.QAbstractSpinBox.PlusMinus)
|
||||
self.inf_spawn_spinBox.setMinimum(0)
|
||||
self.inf_spawn_spinBox.setMaximum(20)
|
||||
self.inf_spawn_spinBox.setProperty("value", 2)
|
||||
self.inf_spawn_spinBox.setProperty("value", 0)
|
||||
self.inf_spawn_spinBox.setObjectName("inf_spawn_spinBox")
|
||||
self.troop_drop_spinBox = QtWidgets.QSpinBox(self.centralwidget)
|
||||
self.troop_drop_spinBox.setGeometry(QtCore.QRect(510, 330, 47, 31))
|
||||
self.troop_drop_spinBox.setGeometry(QtCore.QRect(510, 300, 51, 31))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(12)
|
||||
self.troop_drop_spinBox.setFont(font)
|
||||
@ -303,23 +303,23 @@ class Ui_MainWindow(object):
|
||||
self.troop_drop_spinBox.setMaximum(10)
|
||||
self.troop_drop_spinBox.setProperty("value", 4)
|
||||
self.troop_drop_spinBox.setObjectName("troop_drop_spinBox")
|
||||
self.force_offroad_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.force_offroad_checkBox.setGeometry(QtCore.QRect(960, 548, 161, 24))
|
||||
self.random_weather_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.random_weather_checkBox.setGeometry(QtCore.QRect(980, 420, 211, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.force_offroad_checkBox.setFont(font)
|
||||
self.force_offroad_checkBox.setChecked(False)
|
||||
self.force_offroad_checkBox.setTristate(False)
|
||||
self.force_offroad_checkBox.setObjectName("force_offroad_checkBox")
|
||||
self.random_weather_checkBox.setFont(font)
|
||||
self.random_weather_checkBox.setChecked(False)
|
||||
self.random_weather_checkBox.setTristate(False)
|
||||
self.random_weather_checkBox.setObjectName("random_weather_checkBox")
|
||||
self.label_3 = QtWidgets.QLabel(self.centralwidget)
|
||||
self.label_3.setGeometry(QtCore.QRect(570, 330, 281, 23))
|
||||
self.label_3.setGeometry(QtCore.QRect(570, 300, 281, 23))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.label_3.setFont(font)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.apcs_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(990, 180, 251, 27))
|
||||
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(980, 180, 251, 27))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
@ -328,7 +328,7 @@ class Ui_MainWindow(object):
|
||||
self.apcs_spawn_checkBox.setObjectName("apcs_spawn_checkBox")
|
||||
self.generateButton = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.generateButton.setEnabled(True)
|
||||
self.generateButton.setGeometry(QtCore.QRect(710, 600, 231, 51))
|
||||
self.generateButton.setGeometry(QtCore.QRect(750, 600, 231, 51))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(8)
|
||||
font.setBold(True)
|
||||
@ -336,7 +336,7 @@ class Ui_MainWindow(object):
|
||||
self.generateButton.setStyleSheet("")
|
||||
self.generateButton.setObjectName("generateButton")
|
||||
self.farp_always = QtWidgets.QRadioButton(self.centralwidget)
|
||||
self.farp_always.setGeometry(QtCore.QRect(510, 480, 261, 24))
|
||||
self.farp_always.setGeometry(QtCore.QRect(500, 431, 261, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.farp_always.setFont(font)
|
||||
@ -345,14 +345,14 @@ class Ui_MainWindow(object):
|
||||
self.farp_buttonGroup.setObjectName("farp_buttonGroup")
|
||||
self.farp_buttonGroup.addButton(self.farp_always)
|
||||
self.farp_never = QtWidgets.QRadioButton(self.centralwidget)
|
||||
self.farp_never.setGeometry(QtCore.QRect(510, 540, 271, 24))
|
||||
self.farp_never.setGeometry(QtCore.QRect(500, 491, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.farp_never.setFont(font)
|
||||
self.farp_never.setObjectName("farp_never")
|
||||
self.farp_buttonGroup.addButton(self.farp_never)
|
||||
self.farp_gunits = QtWidgets.QRadioButton(self.centralwidget)
|
||||
self.farp_gunits.setGeometry(QtCore.QRect(510, 509, 261, 24))
|
||||
self.farp_gunits.setGeometry(QtCore.QRect(500, 460, 261, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.farp_gunits.setFont(font)
|
||||
@ -397,7 +397,7 @@ class Ui_MainWindow(object):
|
||||
self.rateButton1.setText("")
|
||||
self.rateButton1.setObjectName("rateButton1")
|
||||
self.hotstart_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.hotstart_checkBox.setGeometry(QtCore.QRect(960, 430, 271, 24))
|
||||
self.hotstart_checkBox.setGeometry(QtCore.QRect(980, 520, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.hotstart_checkBox.setFont(font)
|
||||
@ -440,6 +440,21 @@ class Ui_MainWindow(object):
|
||||
self.rateButton5.setStyleSheet("border-image:url(\'../assets/star_full.png\');")
|
||||
self.rateButton5.setText("")
|
||||
self.rateButton5.setObjectName("rateButton5")
|
||||
self.time_comboBox = QtWidgets.QComboBox(self.centralwidget)
|
||||
self.time_comboBox.setGeometry(QtCore.QRect(980, 370, 161, 33))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(False)
|
||||
self.time_comboBox.setFont(font)
|
||||
self.time_comboBox.setObjectName("time_comboBox")
|
||||
self.farp_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
self.farp_spawn_checkBox.setGeometry(QtCore.QRect(980, 550, 271, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
self.farp_spawn_checkBox.setFont(font)
|
||||
self.farp_spawn_checkBox.setChecked(False)
|
||||
self.farp_spawn_checkBox.setTristate(False)
|
||||
self.farp_spawn_checkBox.setObjectName("farp_spawn_checkBox")
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 29))
|
||||
@ -564,10 +579,10 @@ class Ui_MainWindow(object):
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "RotorOps Mission Generator"))
|
||||
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.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.logistics_crates_checkBox.setStatusTip(_translate("MainWindow", "Enable a base or FARP near the start position that can spawn CTLD crates for building ground units and air defenses. Sling load the logistics containers to create new logistics sites."))
|
||||
self.logistics_crates_checkBox.setText(_translate("MainWindow", "Logistics Base"))
|
||||
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. No effect if Blue on defense."))
|
||||
self.zone_sams_checkBox.setText(_translate("MainWindow", "Protect Inactive Zones"))
|
||||
self.red_forces_label.setText(_translate("MainWindow", "Red Forces:"))
|
||||
self.scenario_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own templates that include mission options like kneeboards, briefings, weather, static units, triggers, scripts, etc."))
|
||||
self.description_textBrowser.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
|
||||
@ -575,6 +590,7 @@ class Ui_MainWindow(object):
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:\'Segoe UI\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:10pt;\">Provide close air support for our convoys as we take back Las Vegas from the enemy!</span></p></body></html>"))
|
||||
self.defense_checkBox.setStatusTip(_translate("MainWindow", "Turn the tables and defend your zones against the enemy\'s attack."))
|
||||
self.defense_checkBox.setText(_translate("MainWindow", "Blue on Defense"))
|
||||
self.redqty_spinBox.setStatusTip(_translate("MainWindow", "Red vehicle groups per staging or conflict zone."))
|
||||
self.redforces_comboBox.setStatusTip(_translate("MainWindow", "Tip: You can create your own custom ground forces groups to be automatically generated."))
|
||||
@ -602,20 +618,20 @@ class Ui_MainWindow(object):
|
||||
self.tankers_checkBox.setText(_translate("MainWindow", "Friendly Tankers"))
|
||||
self.voiceovers_checkBox.setStatusTip(_translate("MainWindow", "Voiceovers from the ground commander. Helps keep focus on the active zone."))
|
||||
self.voiceovers_checkBox.setText(_translate("MainWindow", "Voiceovers"))
|
||||
self.smoke_pickup_zone_checkBox.setStatusTip(_translate("MainWindow", "Infinite troop pickup zones will be marked with blue smoke."))
|
||||
self.smoke_pickup_zone_checkBox.setStatusTip(_translate("MainWindow", "Troop pickup zones and FARPs will be marked with blue smoke."))
|
||||
self.smoke_pickup_zone_checkBox.setText(_translate("MainWindow", "Smoke at Troop Pickup Zones"))
|
||||
self.game_status_checkBox.setStatusTip(_translate("MainWindow", "Enable an onscreen zone status display. This helps keep focus on the active conflict zone."))
|
||||
self.game_status_checkBox.setText(_translate("MainWindow", "Game Status Display"))
|
||||
self.label.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
|
||||
self.label.setText(_translate("MainWindow", "Infantry Spawns per zone"))
|
||||
self.label.setStatusTip(_translate("MainWindow", "Total number of infantry groups to spawn per game."))
|
||||
self.label.setText(_translate("MainWindow", "Infantry Spawns"))
|
||||
self.inf_spawn_spinBox.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
|
||||
self.troop_drop_spinBox.setStatusTip(_translate("MainWindow", "The number of troop drops per transport helicopter flight."))
|
||||
self.force_offroad_checkBox.setStatusTip(_translate("MainWindow", "May help prevent long travel times or pathfinding issues. "))
|
||||
self.force_offroad_checkBox.setText(_translate("MainWindow", "Force Offroad"))
|
||||
self.random_weather_checkBox.setStatusTip(_translate("MainWindow", "Random weather preset will be applied."))
|
||||
self.random_weather_checkBox.setText(_translate("MainWindow", "Random Weather"))
|
||||
self.label_3.setStatusTip(_translate("MainWindow", "The number of troop drops per transport helicopter flight."))
|
||||
self.label_3.setText(_translate("MainWindow", "Transport Drop Points"))
|
||||
self.apcs_spawn_checkBox.setStatusTip(_translate("MainWindow", "Friendly/enemy APCs will drop infantry when reaching a new conflict zone. Disables infinite troop pickups from conflict zones (you must pick up existing troops)."))
|
||||
self.apcs_spawn_checkBox.setText(_translate("MainWindow", "Dynamic Troops"))
|
||||
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.generateButton.setStatusTip(_translate("MainWindow", "Click to generate mission."))
|
||||
self.generateButton.setText(_translate("MainWindow", "GENERATE MISSION"))
|
||||
self.farp_always.setStatusTip(_translate("MainWindow", "Always spawn a FARP in defeated conflict zones."))
|
||||
@ -627,12 +643,15 @@ class Ui_MainWindow(object):
|
||||
self.nextScenario_pushButton.setText(_translate("MainWindow", ">"))
|
||||
self.prevScenario_pushButton.setText(_translate("MainWindow", "<"))
|
||||
self.rateButton1.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.hotstart_checkBox.setStatusTip(_translate("MainWindow", "Player helicopters start with engines running on the ground. No effect if player slots says \'Locked to scenario\'"))
|
||||
self.hotstart_checkBox.setStatusTip(_translate("MainWindow", "Player helicopters start with engines running on the ground. No effect for FARP spawns or if player slots says \'Locked to scenario\'"))
|
||||
self.hotstart_checkBox.setText(_translate("MainWindow", "Player Hotstart"))
|
||||
self.rateButton2.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.rateButton3.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.rateButton4.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.rateButton5.setStatusTip(_translate("MainWindow", "Submit a review for this mission scenario."))
|
||||
self.time_comboBox.setStatusTip(_translate("MainWindow", "Mission start time of day. \'Default\' is the start time as defined by the mission template designer."))
|
||||
self.farp_spawn_checkBox.setStatusTip(_translate("MainWindow", "Add helicopter slots where zone FARPs will be built. Helicopters will be empty fuel, requiring the FARP to be established to refuel and rearm."))
|
||||
self.farp_spawn_checkBox.setText(_translate("MainWindow", "Spawns at zone FARPs"))
|
||||
self.menuMap.setTitle(_translate("MainWindow", "Map"))
|
||||
self.menuFilter.setTitle(_translate("MainWindow", "Filter"))
|
||||
self.menuPreferences.setTitle(_translate("MainWindow", "Preferences"))
|
||||
@ -658,8 +677,8 @@ class Ui_MainWindow(object):
|
||||
self.action_downloadButton.setToolTip(_translate("MainWindow", "_downloadButton"))
|
||||
self.action_rateButton1.setText(_translate("MainWindow", "_rateButton1"))
|
||||
self.action_rateButton1.setToolTip(_translate("MainWindow", "_rateButton1"))
|
||||
self.actionSingle_Player.setText(_translate("MainWindow", "Single-Player"))
|
||||
self.actionCo_Op.setText(_translate("MainWindow", "Co-Op"))
|
||||
self.actionSingle_Player.setText(_translate("MainWindow", "Single-Player Only"))
|
||||
self.actionCo_Op.setText(_translate("MainWindow", "Co-Op Only"))
|
||||
self.actionMapMenu.setText(_translate("MainWindow", "actionMapMenu"))
|
||||
self.actionFilterMenu.setText(_translate("MainWindow", "FilterMenu"))
|
||||
self.action_rateButton2.setText(_translate("MainWindow", "_rateButton2"))
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
<widget class="QCheckBox" name="logistics_crates_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>990</x>
|
||||
<x>980</x>
|
||||
<y>211</y>
|
||||
<width>251</width>
|
||||
<height>28</height>
|
||||
@ -66,10 +66,10 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Enable CTLD logistics crates for building ground units and air defenses. Pickup logistics containers to create new logistics sites.</string>
|
||||
<string>Enable a base or FARP near the start position that can spawn CTLD crates for building ground units and air defenses. Sling load the logistics containers to create new logistics sites.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Logistics</string>
|
||||
<string>Logistics Base</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@ -78,7 +78,7 @@
|
||||
<widget class="QCheckBox" name="zone_sams_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>990</x>
|
||||
<x>980</x>
|
||||
<y>320</y>
|
||||
<width>241</width>
|
||||
<height>28</height>
|
||||
@ -91,10 +91,10 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed.</string>
|
||||
<string>Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed. No effect if Blue on defense.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Inactive Zone SAMs</string>
|
||||
<string>Protect Inactive Zones</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="red_forces_label">
|
||||
@ -191,17 +191,20 @@ p, li { white-space: pre-wrap; }
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>470</x>
|
||||
<y>120</y>
|
||||
<y>130</y>
|
||||
<width>156</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<pointsize>11</pointsize>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Turn the tables and defend your zones against the enemy's attack.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Blue on Defense</string>
|
||||
</property>
|
||||
@ -289,8 +292,8 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QComboBox" name="slot_template_comboBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>960</x>
|
||||
<y>384</y>
|
||||
<x>980</x>
|
||||
<y>474</y>
|
||||
<width>271</width>
|
||||
<height>33</height>
|
||||
</rect>
|
||||
@ -564,7 +567,7 @@ p, li { white-space: pre-wrap; }
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="scenario_label_7">
|
||||
@ -592,8 +595,8 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>840</x>
|
||||
<y>390</y>
|
||||
<x>860</x>
|
||||
<y>480</y>
|
||||
<width>111</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
@ -611,8 +614,8 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QLabel" name="scenario_label_9">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>490</x>
|
||||
<y>450</y>
|
||||
<x>480</x>
|
||||
<y>401</y>
|
||||
<width>251</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
@ -629,7 +632,7 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QCheckBox" name="awacs_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>990</x>
|
||||
<x>980</x>
|
||||
<y>246</y>
|
||||
<width>241</width>
|
||||
<height>28</height>
|
||||
@ -654,7 +657,7 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QCheckBox" name="tankers_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>990</x>
|
||||
<x>980</x>
|
||||
<y>282</y>
|
||||
<width>241</width>
|
||||
<height>28</height>
|
||||
@ -679,10 +682,10 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QCheckBox" name="voiceovers_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>960</x>
|
||||
<y>517</y>
|
||||
<x>500</x>
|
||||
<y>594</y>
|
||||
<width>171</width>
|
||||
<height>24</height>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -703,10 +706,10 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QCheckBox" name="smoke_pickup_zone_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>960</x>
|
||||
<y>460</y>
|
||||
<width>271</width>
|
||||
<height>24</height>
|
||||
<x>500</x>
|
||||
<y>541</y>
|
||||
<width>231</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -715,7 +718,7 @@ p, li { white-space: pre-wrap; }
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Infinite troop pickup zones will be marked with blue smoke.</string>
|
||||
<string>Troop pickup zones and FARPs will be marked with blue smoke.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Smoke at Troop Pickup Zones</string>
|
||||
@ -727,10 +730,10 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QCheckBox" name="game_status_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>960</x>
|
||||
<y>490</y>
|
||||
<width>271</width>
|
||||
<height>24</height>
|
||||
<x>500</x>
|
||||
<y>570</y>
|
||||
<width>221</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -755,7 +758,7 @@ p, li { white-space: pre-wrap; }
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>570</x>
|
||||
<y>380</y>
|
||||
<y>340</y>
|
||||
<width>261</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
@ -767,18 +770,18 @@ p, li { white-space: pre-wrap; }
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>This value is multiplied by the number of spawn zones in the mission template.</string>
|
||||
<string>Total number of infantry groups to spawn per game.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Infantry Spawns per zone</string>
|
||||
<string>Infantry Spawns</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QSpinBox" name="inf_spawn_spinBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>510</x>
|
||||
<y>380</y>
|
||||
<width>47</width>
|
||||
<y>340</y>
|
||||
<width>51</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -800,15 +803,15 @@ p, li { white-space: pre-wrap; }
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QSpinBox" name="troop_drop_spinBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>510</x>
|
||||
<y>330</y>
|
||||
<width>47</width>
|
||||
<y>300</y>
|
||||
<width>51</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -833,12 +836,12 @@ p, li { white-space: pre-wrap; }
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="force_offroad_checkBox">
|
||||
<widget class="QCheckBox" name="random_weather_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>960</x>
|
||||
<y>548</y>
|
||||
<width>161</width>
|
||||
<x>980</x>
|
||||
<y>420</y>
|
||||
<width>211</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -848,10 +851,10 @@ p, li { white-space: pre-wrap; }
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>May help prevent long travel times or pathfinding issues. </string>
|
||||
<string>Random weather preset will be applied.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Force Offroad</string>
|
||||
<string>Random Weather</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
@ -864,7 +867,7 @@ p, li { white-space: pre-wrap; }
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>570</x>
|
||||
<y>330</y>
|
||||
<y>300</y>
|
||||
<width>281</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
@ -885,7 +888,7 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QCheckBox" name="apcs_spawn_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>990</x>
|
||||
<x>980</x>
|
||||
<y>180</y>
|
||||
<width>251</width>
|
||||
<height>27</height>
|
||||
@ -898,10 +901,10 @@ p, li { white-space: pre-wrap; }
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Friendly/enemy APCs will drop infantry when reaching a new conflict zone. Disables infinite troop pickups from conflict zones (you must pick up existing troops).</string>
|
||||
<string>Friendly/enemy APCs will drop infantry when reaching a new conflict zone. </string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dynamic Troops</string>
|
||||
<string>APCs Spawn Infantry</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@ -913,7 +916,7 @@ p, li { white-space: pre-wrap; }
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>710</x>
|
||||
<x>750</x>
|
||||
<y>600</y>
|
||||
<width>231</width>
|
||||
<height>51</height>
|
||||
@ -938,8 +941,8 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QRadioButton" name="farp_always">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>510</x>
|
||||
<y>480</y>
|
||||
<x>500</x>
|
||||
<y>431</y>
|
||||
<width>261</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
@ -962,8 +965,8 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QRadioButton" name="farp_never">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>510</x>
|
||||
<y>540</y>
|
||||
<x>500</x>
|
||||
<y>491</y>
|
||||
<width>271</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
@ -986,8 +989,8 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QRadioButton" name="farp_gunits">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>510</x>
|
||||
<y>509</y>
|
||||
<x>500</x>
|
||||
<y>460</y>
|
||||
<width>261</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
@ -1131,8 +1134,8 @@ p, li { white-space: pre-wrap; }
|
||||
<widget class="QCheckBox" name="hotstart_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>960</x>
|
||||
<y>430</y>
|
||||
<x>980</x>
|
||||
<y>520</y>
|
||||
<width>271</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
@ -1143,7 +1146,7 @@ p, li { white-space: pre-wrap; }
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Player helicopters start with engines running on the ground. No effect if player slots says 'Locked to scenario'</string>
|
||||
<string>Player helicopters start with engines running on the ground. No effect for FARP spawns or if player slots says 'Locked to scenario'</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Player Hotstart</string>
|
||||
@ -1263,6 +1266,52 @@ p, li { white-space: pre-wrap; }
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="time_comboBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>980</x>
|
||||
<y>370</y>
|
||||
<width>161</width>
|
||||
<height>33</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Mission start time of day. 'Default' is the start time as defined by the mission template designer.</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="farp_spawn_checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>980</x>
|
||||
<y>550</y>
|
||||
<width>271</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Add helicopter slots where zone FARPs will be built. Helicopters will be empty fuel, requiring the FARP to be established to refuel and rearm.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Spawns at zone FARPs</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
@ -1471,7 +1520,7 @@ p, li { white-space: pre-wrap; }
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Single-Player</string>
|
||||
<string>Single-Player Only</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCo_Op">
|
||||
@ -1482,7 +1531,7 @@ p, li { white-space: pre-wrap; }
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Co-Op</string>
|
||||
<string>Co-Op Only</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMapMenu">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,6 @@ def triggerSetup(rops, options):
|
||||
# Add the first trigger
|
||||
trig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Scripts")
|
||||
trig.rules.append(dcs.condition.TimeAfter(1))
|
||||
#trig.actions.append(dcs.action.DoScriptFile(rops.scripts["mist_4_4_90.lua"]))
|
||||
trig.actions.append(dcs.action.DoScriptFile(rops.scripts["mist_4_5_107_grimm.lua"]))
|
||||
trig.actions.append(dcs.action.DoScriptFile(rops.scripts["Splash_Damage_2_0.lua"]))
|
||||
trig.actions.append(dcs.action.DoScriptFile(rops.scripts["CTLD.lua"]))
|
||||
@ -29,11 +28,13 @@ def triggerSetup(rops, options):
|
||||
"RotorOps.voice_overs = " + lb("voiceovers") + "\n\n" +
|
||||
"RotorOps.zone_status_display = " + lb("game_display") + "\n\n" +
|
||||
"RotorOps.inf_spawn_messages = true\n\n" +
|
||||
"RotorOps.inf_spawns_per_zone = " + lb("inf_spawn_qty") + "\n\n" +
|
||||
"RotorOps.inf_spawns_total = " + lb("inf_spawn_qty") + "\n\n" +
|
||||
"RotorOps.apcs_spawn_infantry = " + lb("apc_spawns_inf") + " \n\n")
|
||||
if not options["smoke_pickup_zones"]:
|
||||
script = script + 'RotorOps.pickup_zone_smoke = "none"\n\n'
|
||||
trig.actions.append(dcs.action.DoScript(dcs.action.String((script))))
|
||||
if options["script"]:
|
||||
trig.actions.append(dcs.action.DoScript(dcs.action.String((options["script"]))))
|
||||
rops.m.triggerrules.triggers.append(trig)
|
||||
|
||||
# Add the second trigger
|
||||
@ -50,7 +51,8 @@ def triggerSetup(rops, options):
|
||||
|
||||
rops.m.triggerrules.triggers.append(trig)
|
||||
|
||||
# Add the third trigger
|
||||
# Add the start trigger
|
||||
if options["start_trigger"] is not False:
|
||||
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict Start")
|
||||
trig.rules.append(dcs.condition.TimeAfter(10))
|
||||
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.startConflict(100)")))
|
||||
@ -81,6 +83,19 @@ def triggerSetup(rops, options):
|
||||
dcs.action.String("Group.destroy(Group.getByName('Static " + zone_name + " Protection SAM'))")))
|
||||
rops.m.triggerrules.triggers.append(z_sams_trig)
|
||||
|
||||
# Deactivate zone FARPs and player slots in defensive mode:
|
||||
# this will also deactivate players already in the air.
|
||||
# if options["defending"]:
|
||||
# for index, zone_name in enumerate(rops.conflict_zones):
|
||||
# z_farps_trig = dcs.triggers.TriggerOnce(comment="Deactivate " + zone_name + " FARP")
|
||||
# z_farps_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1))
|
||||
# z_farps_trig.actions.append(dcs.action.DeactivateGroup(rops.m.country(jtf_blue).find_group(zone_name + " FARP Static").id))
|
||||
# for group in rops.all_zones[zone_name].player_helo_spawns:
|
||||
# z_farps_trig.actions.append(
|
||||
# dcs.action.DeactivateGroup(
|
||||
# group.id))
|
||||
# rops.m.triggerrules.triggers.append(z_farps_trig)
|
||||
|
||||
# Zone FARPS always
|
||||
if options["zone_farps"] == "farp_always" and not options["defending"]:
|
||||
for index, zone_name in enumerate(rops.conflict_zones):
|
||||
@ -92,9 +107,13 @@ def triggerSetup(rops, options):
|
||||
z_farps_trig.rules.append(dcs.condition.FlagEquals(game_flag, index + 1))
|
||||
z_farps_trig.actions.append(
|
||||
dcs.action.ActivateGroup(rops.m.country(jtf_blue).find_group(previous_zone + " FARP Static").id))
|
||||
# z_farps_trig.actions.append(dcs.action.SoundToAll(str(rops.res_map['forward_base_established.ogg'])))
|
||||
# Activate late-activated helicopters at FARPs. Doesn't work consistently
|
||||
# for group in rops.all_zones[previous_zone].player_helo_spawns:
|
||||
# z_farps_trig.actions.append(
|
||||
# dcs.action.ActivateGroup(
|
||||
# group.id))
|
||||
z_farps_trig.actions.append(dcs.action.DoScript(dcs.action.String(
|
||||
"RotorOps.farpEstablished(" + str(index) + ")")))
|
||||
"RotorOps.farpEstablished(" + str(index) + ", '" + previous_zone + "_FARP')")))
|
||||
rops.m.triggerrules.triggers.append(z_farps_trig)
|
||||
|
||||
# Zone FARPS conditional on staged units remaining
|
||||
@ -111,9 +130,13 @@ def triggerSetup(rops, options):
|
||||
"--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(rops.m.country(jtf_blue).find_group(previous_zone + " FARP Static").id))
|
||||
# z_farps_trig.actions.append(dcs.action.SoundToAll(str(rops.res_map['forward_base_established.ogg'])))
|
||||
# Activate late-activated helicopters at FARPs. Doesn't work consistently
|
||||
# for group in rops.all_zones[previous_zone].player_helo_spawns:
|
||||
# z_farps_trig.actions.append(
|
||||
# dcs.action.ActivateGroup(
|
||||
# group.id))
|
||||
z_farps_trig.actions.append(dcs.action.DoScript(dcs.action.String(
|
||||
"RotorOps.farpEstablished(" + str(index) + ")")))
|
||||
"RotorOps.farpEstablished(" + str(index) + ", '" + previous_zone + "_FARP')")))
|
||||
rops.m.triggerrules.triggers.append(z_farps_trig)
|
||||
|
||||
# Add attack helos triggers
|
||||
@ -156,17 +179,23 @@ def triggerSetup(rops, options):
|
||||
rops.m.triggerrules.triggers.append(z_weak_trig)
|
||||
|
||||
# Add game won/lost triggers
|
||||
|
||||
|
||||
# Add game won triggers
|
||||
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict WON")
|
||||
trig.rules.append(dcs.condition.FlagEquals(game_flag, 99))
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is WON")))
|
||||
if options["end_trigger"] is not False:
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("RotorOps.gameMsg(RotorOps.gameMsgs.success)")))
|
||||
rops.m.triggerrules.triggers.append(trig)
|
||||
|
||||
# Add game lost triggers
|
||||
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict LOST")
|
||||
trig.rules.append(dcs.condition.FlagEquals(game_flag, 98))
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is LOST")))
|
||||
trig.actions.append(
|
||||
dcs.action.DoScript(dcs.action.String("RotorOps.gameMsg(RotorOps.gameMsgs.failure)")))
|
||||
if options["end_trigger"] is not False:
|
||||
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.gameMsg(RotorOps.gameMsgs.failure)")))
|
||||
rops.m.triggerrules.triggers.append(trig)
|
||||
@ -1,12 +1,15 @@
|
||||
import math
|
||||
import dcs
|
||||
from MissionGenerator import logger
|
||||
import os
|
||||
|
||||
|
||||
class ImportObjects:
|
||||
|
||||
def __init__(self, mizfile):
|
||||
self.pad_unit = True # todo: use this to hold a unit for helicopter placement on ships ie flight_group_from_unit
|
||||
if not mizfile or not os.path.exists(mizfile):
|
||||
raise Exception("Cannot find required file: " + str(mizfile))
|
||||
logger.info("Importing objects from " + mizfile)
|
||||
self.source_mission = dcs.mission.Mission()
|
||||
self.source_mission.load_file(mizfile)
|
||||
@ -32,7 +35,6 @@ class ImportObjects:
|
||||
self.copyVehicles(mission, dest_country_name, dest_name, dest_point, dest_heading), \
|
||||
self.copyHelicopters(mission, dest_country_name, dest_name, dest_point, dest_heading)
|
||||
|
||||
|
||||
def anchorByGroupName(self, group_name):
|
||||
group = self.source_mission.find_group(group_name)
|
||||
if group:
|
||||
@ -49,7 +51,10 @@ class ImportObjects:
|
||||
coalition = self.source_mission.coalition.get(side)
|
||||
for country_name in coalition.countries:
|
||||
|
||||
group_types = [coalition.countries[country_name].static_group, coalition.countries[country_name].vehicle_group, coalition.countries[country_name].helicopter_group, coalition.countries[country_name].plane_group,
|
||||
group_types = [coalition.countries[country_name].static_group,
|
||||
coalition.countries[country_name].vehicle_group,
|
||||
coalition.countries[country_name].helicopter_group,
|
||||
coalition.countries[country_name].plane_group,
|
||||
coalition.countries[country_name].ship_group]
|
||||
|
||||
for index, group_type in enumerate(group_types):
|
||||
@ -66,7 +71,6 @@ class ImportObjects:
|
||||
elif index == 4:
|
||||
logger.warn(group.name + ": Ships not available for import")
|
||||
|
||||
|
||||
def copyStatics(self, mission, dest_country_name, dest_name, dest_point=None, dest_heading=0):
|
||||
logger.info("Copying " + str(len(self.statics)) + " static objects as " + dest_name)
|
||||
new_groups = []
|
||||
@ -77,10 +81,8 @@ class ImportObjects:
|
||||
# Statics
|
||||
statics_copy = self.statics.copy()
|
||||
for group in statics_copy:
|
||||
|
||||
self.groupToPoint(group, self.source_point, dest_point, self.source_heading, dest_heading)
|
||||
|
||||
|
||||
class temp(dcs.unittype.StaticType):
|
||||
id = group.units[0].type
|
||||
name = group.units[0].name
|
||||
@ -89,7 +91,6 @@ class ImportObjects:
|
||||
can_cargo = group.units[0].can_cargo
|
||||
mass = group.units[0].mass
|
||||
|
||||
|
||||
ng = mission.static_group(mission.country(dest_country_name),
|
||||
dest_name + " " + group.name,
|
||||
temp,
|
||||
@ -104,9 +105,6 @@ class ImportObjects:
|
||||
|
||||
return new_groups
|
||||
|
||||
|
||||
|
||||
|
||||
def copyVehicles(self, mission, dest_country_name, dest_name, dest_point=None, dest_heading=0):
|
||||
logger.info("Copying " + str(len(self.vehicles)) + " vehicle groups as " + dest_name)
|
||||
new_groups = []
|
||||
@ -127,25 +125,25 @@ class ImportObjects:
|
||||
group.units[0].position,
|
||||
group.units[0].heading)
|
||||
|
||||
new_groups.append(ng) # will this hold units we add later?
|
||||
# ng.units[0].livery_id = group.units[0].livery_id
|
||||
new_groups.append(ng)
|
||||
|
||||
else:
|
||||
|
||||
u = mission.vehicle(dest_name + " " + group.units[i].name, dcs.vehicles.vehicle_map[group.units[i].type])
|
||||
u = mission.vehicle(dest_name + " " + group.units[i].name,
|
||||
dcs.vehicles.vehicle_map[group.units[i].type])
|
||||
u.position = group.units[i].position
|
||||
u.heading = group.units[i].heading
|
||||
# u.livery_id = group.units[i].livery_id
|
||||
ng.add_unit(u)
|
||||
|
||||
return new_groups
|
||||
|
||||
|
||||
def copyHelicopters(self, mission, dest_country_name, dest_name, dest_point=None, dest_heading=0):
|
||||
def copyHelicopters(self, mission, dest_country_name, dest_name, dest_point, dest_heading=0,
|
||||
start_type=dcs.mission.StartType.Cold):
|
||||
logger.info("Copying " + str(len(self.helicopters)) + " helicopters as " + dest_name)
|
||||
new_groups = []
|
||||
|
||||
if not dest_point:
|
||||
dest_point = dcs.Point(mission.terrain.bullseye_blue["x"], mission.terrain.bullseye_blue["y"])
|
||||
|
||||
helicopters_copy = self.helicopters.copy()
|
||||
for group in helicopters_copy:
|
||||
|
||||
@ -158,39 +156,46 @@ class ImportObjects:
|
||||
# trying to move the units into position after adding the flight group moves the 2D graphic of the helicopter, but the unit marker remains stacked on top
|
||||
# of the unit marker in ME
|
||||
# farp = mission.country(country_name).find_group(self.pad_unit.name)
|
||||
|
||||
farp = mission.farp(mission.country(dest_country_name), dest_name + " " + group.name + " Pad", group.units[0].position, hidden=True, dead=False,
|
||||
farp_type=dcs.unit.InvisibleFARP)
|
||||
|
||||
|
||||
|
||||
ng = mission.flight_group_from_unit(mission.country(dest_country_name),
|
||||
#
|
||||
# farp = mission.farp(mission.country(dest_country_name), dest_name + " " + group.name + " Pad", group.units[0].position, hidden=True, dead=False,
|
||||
# farp_type=dcs.unit.InvisibleFARP)
|
||||
#
|
||||
#
|
||||
# ng = mission.flight_group_from_unit(mission.country(dest_country_name),
|
||||
# dest_name + " " + group.name,
|
||||
# dcs.helicopters.helicopter_map[group.units[0].type],
|
||||
# farp,
|
||||
# group_size=1, start_type=start_type)
|
||||
ng = mission.flight_group(mission.country(dest_country_name),
|
||||
dest_name + " " + group.name,
|
||||
dcs.helicopters.helicopter_map[group.units[0].type],
|
||||
farp,
|
||||
group_size=1)
|
||||
airport=None,
|
||||
position=group.units[0].position,
|
||||
group_size=1, start_type=start_type)
|
||||
|
||||
if start_type == dcs.mission.StartType.Warm:
|
||||
ng.points[0].action = dcs.point.PointAction.FromGroundAreaHot
|
||||
ng.points[0].type = "TakeOffGroundHot"
|
||||
else:
|
||||
ng.points[0].action = dcs.point.PointAction.FromGroundArea
|
||||
ng.points[0].type = "TakeOffGround"
|
||||
ng.units[0].heading = group.units[0].heading
|
||||
ng.units[0].skill = group.units[0].skill
|
||||
ng.units[0].livery_id = group.units[0].livery_id
|
||||
ng.units[0].pylons = group.units[0].pylons
|
||||
|
||||
ng.units[0].fuel = group.units[0].fuel
|
||||
ng.units[0].gun = group.units[0].gun
|
||||
ng.units[0].hardpoint_racks = group.units[0].hardpoint_racks
|
||||
new_groups.append(ng)
|
||||
else:
|
||||
logger.warn("No pad unit (ie FARP, carrier) found, so can't add helicopters.")
|
||||
|
||||
return new_groups
|
||||
|
||||
|
||||
def copyVehiclesAsGroup(self, mission, dest_country_name, dest_name, dest_point=None, dest_heading=0):
|
||||
def copyVehiclesAsGroup(self, mission, dest_country_name, dest_name, dest_point, dest_heading=0):
|
||||
logger.info("Copying " + str(len(self.vehicles)) + " vehicle groups as single group name: " + dest_name)
|
||||
new_group = None
|
||||
|
||||
if not dest_point:
|
||||
dest_point = dcs.Point(mission.terrain.bullseye_blue["x"], mission.terrain.bullseye_blue["y"])
|
||||
|
||||
unit_count = 0
|
||||
vehicles_copy = self.vehicles.copy()
|
||||
for group in vehicles_copy:
|
||||
@ -198,29 +203,32 @@ class ImportObjects:
|
||||
for i, unit in enumerate(group.units):
|
||||
|
||||
if unit_count == 0:
|
||||
print("Group:" + group.name)
|
||||
# print("Group:" + group.name)
|
||||
new_group = mission.vehicle_group(mission.country(dest_country_name),
|
||||
dest_name,
|
||||
dcs.vehicles.vehicle_map[group.units[0].type],
|
||||
group.units[0].position,
|
||||
group.units[0].heading)
|
||||
unit_count = unit_count + 1
|
||||
# new_group.units[0].livery_id = group.units[0].livery_id
|
||||
|
||||
else:
|
||||
|
||||
print("Unit:" + group.units[i].name)
|
||||
u = mission.vehicle(dest_name + " " + group.units[i].name, dcs.vehicles.vehicle_map[group.units[i].type])
|
||||
# print("Unit:" + group.units[i].name)
|
||||
u = mission.vehicle(dest_name + " " + group.units[i].name,
|
||||
dcs.vehicles.vehicle_map[group.units[i].type])
|
||||
u.position = group.units[i].position
|
||||
u.heading = group.units[i].heading
|
||||
|
||||
# u.livery_id = group.units[i].livery_id
|
||||
new_group.add_unit(u)
|
||||
|
||||
unit_count = unit_count + 1
|
||||
print("Made a group with units: " + str(unit_count))
|
||||
print("group actually has units: " + str(len(new_group.units)))
|
||||
# print("group actually has units: " + str(len(new_group.units)))
|
||||
|
||||
return new_group
|
||||
|
||||
|
||||
@staticmethod
|
||||
def groupToPoint(group, src_point, dest_point, src_heading=0, dest_heading=0):
|
||||
for unit in group.units:
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
from tokenize import String
|
||||
|
||||
import dcs
|
||||
import dcs.cloud_presets
|
||||
import os
|
||||
import random
|
||||
|
||||
|
||||
import RotorOpsGroups
|
||||
import RotorOpsUnits
|
||||
import RotorOpsUtils
|
||||
@ -18,6 +18,7 @@ from MissionGenerator import directories
|
||||
jtf_red = "Combined Joint Task Forces Red"
|
||||
jtf_blue = "Combined Joint Task Forces Blue"
|
||||
|
||||
|
||||
class RotorOpsMission:
|
||||
|
||||
def __init__(self):
|
||||
@ -26,10 +27,11 @@ class RotorOpsMission:
|
||||
self.conflict_zones = {}
|
||||
self.staging_zones = {}
|
||||
self.spawn_zones = {}
|
||||
self.all_zones = {}
|
||||
self.scripts = {}
|
||||
self.res_map = {}
|
||||
self.config = None
|
||||
|
||||
self.config = None # not used
|
||||
self.imports = None
|
||||
|
||||
class RotorOpsZone:
|
||||
def __init__(self, name: str, flag: int, position: dcs.point, size: int):
|
||||
@ -38,6 +40,8 @@ class RotorOpsMission:
|
||||
self.position = position
|
||||
self.size = size
|
||||
|
||||
self.player_helo_spawns = []
|
||||
self.base_position = position
|
||||
|
||||
def getMission(self):
|
||||
return self.m
|
||||
@ -62,7 +66,6 @@ class RotorOpsMission:
|
||||
key = self.m.map_resource.add_resource_file(filename)
|
||||
self.res_map[filename] = key
|
||||
|
||||
|
||||
# add all of our lua scripts
|
||||
os.chdir(script_directory)
|
||||
path = os.getcwd()
|
||||
@ -128,31 +131,54 @@ class RotorOpsMission:
|
||||
logger.info("Looking for mission files in " + os.getcwd())
|
||||
|
||||
window.statusBar().showMessage("Loading scenario mission", 10000)
|
||||
|
||||
self.m.load_file(options["scenario_file"])
|
||||
self.addMods()
|
||||
self.importObjects()
|
||||
|
||||
#todo: test
|
||||
# Add countries if they're missing
|
||||
if not self.m.country(jtf_red):
|
||||
self.m.coalition.get("red").add_country(dcs.countries.CombinedJointTaskForcesRed())
|
||||
if not self.m.country(jtf_blue):
|
||||
self.m.coalition.get("blue").add_country(dcs.countries.CombinedJointTaskForcesBlue())
|
||||
if not self.m.country(
|
||||
dcs.countries.UnitedNationsPeacekeepers.name):
|
||||
self.m.coalition.get("neutrals").add_country(dcs.countries.UnitedNationsPeacekeepers())
|
||||
|
||||
if not self.m.country(jtf_red) or not self.m.country(jtf_blue) or not self.m.country(dcs.countries.UnitedNationsPeacekeepers.name):
|
||||
failure_msg = "You must include a CombinedJointTaskForcesBlue and CombinedJointTaskForcesRed unit in the scenario template. See the instructions in " + directories.scenarios
|
||||
return {"success": False, "failure_msg": failure_msg}
|
||||
|
||||
# red_forces = self.getUnitsFromMiz(directories.forces + "/red/" + options["red_forces_filename"], "red")
|
||||
# blue_forces = self.getUnitsFromMiz(directories.forces + "/blue/" + options["blue_forces_filename"], "blue")
|
||||
red_forces = self.getUnitsFromMiz(directories.forces + "/" + options["red_forces_filename"], "both")
|
||||
blue_forces = self.getUnitsFromMiz(directories.forces + "/" + options["blue_forces_filename"], "both")
|
||||
|
||||
# Add coalitions (we may be able to add CJTF here instead of requiring templates to have objects of those coalitions)
|
||||
if not self.m.country("Russia"):
|
||||
self.m.coalition.get("red").add_country(dcs.countries.Russia())
|
||||
if not self.m.country("USA"):
|
||||
self.m.coalition.get("blue").add_country(dcs.countries.USA())
|
||||
# blue = self.m.coalition.get("blue")
|
||||
# blue.add_country(dcs.countries.CombinedJointTaskForcesBlue())
|
||||
|
||||
self.addMods()
|
||||
self.importObjects(options)
|
||||
|
||||
red_forces = self.getUnitsFromMiz(options["red_forces_path"], "both")
|
||||
blue_forces = self.getUnitsFromMiz(options["blue_forces_path"], "both")
|
||||
|
||||
# add images to briefing
|
||||
self.m.add_picture_blue(directories.assets + '/briefing1.png')
|
||||
self.m.add_picture_blue(directories.assets + '/briefing2.png')
|
||||
|
||||
# get import objects for generic farps etc
|
||||
self.imports = options["objects"]["imports"]
|
||||
|
||||
activated_farp = None
|
||||
defensive_farp = None
|
||||
logistics_farp = None
|
||||
logistics_base = None
|
||||
zone_protect = None
|
||||
|
||||
for i in self.imports:
|
||||
if i.filename == ("FARP_ACTIVATED_ZONE.miz"):
|
||||
activated_farp = i.path
|
||||
if i.filename == ("FARP_DEFENSIVE_ZONE.miz"):
|
||||
defensive_farp = i.path
|
||||
if i.filename == ("FARP_LOGISTICS_ZONE.miz"):
|
||||
logistics_farp = i.path
|
||||
if i.filename == ("STAGING_LOGISTICS_BASE.miz"):
|
||||
logistics_base = i.path
|
||||
if i.filename == ("ZONE_ACTIVATED_DEFENSE.miz"):
|
||||
zone_protect = i.path
|
||||
|
||||
# it's possible to have import templates with the same filename, but we will let the latest override others
|
||||
# todo: verify we have the required templates
|
||||
|
||||
# add zones to target mission
|
||||
zone_names = ["ALPHA", "BRAVO", "CHARLIE", "DELTA"]
|
||||
@ -160,17 +186,20 @@ class RotorOpsMission:
|
||||
for zone_name in zone_names:
|
||||
for zone in self.m.triggers.zones():
|
||||
if zone.name == zone_name:
|
||||
self.addZone(self.conflict_zones, self.RotorOpsZone(zone_name, zone_flag, zone.position, zone.radius))
|
||||
self.addZone(self.conflict_zones,
|
||||
self.RotorOpsZone(zone_name, zone_flag, zone.position, zone.radius))
|
||||
zone_flag = zone_flag + 1
|
||||
|
||||
|
||||
for zone in self.m.triggers.zones():
|
||||
if zone.name.rfind("STAGING") >= 0:
|
||||
self.addZone(self.all_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
|
||||
if zone_name == "STAGING":
|
||||
self.addZone(self.staging_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
|
||||
continue
|
||||
if zone.name.rfind("STAGING") >= 0: # find additional staging zones
|
||||
self.addZone(self.staging_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
|
||||
elif zone.name.rfind("SPAWN") >= 0:
|
||||
self.addZone(self.spawn_zones, self.RotorOpsZone(zone.name, None, zone.position, zone.radius))
|
||||
|
||||
|
||||
blue_zones = self.staging_zones
|
||||
red_zones = self.conflict_zones
|
||||
if options["defending"]:
|
||||
@ -179,101 +208,238 @@ class RotorOpsMission:
|
||||
# swap airport sides
|
||||
self.swapSides(options)
|
||||
|
||||
|
||||
|
||||
# Populate Red zones with ground units
|
||||
window.statusBar().showMessage("Populating units into mission...", 10000)
|
||||
start_type = dcs.mission.StartType.Cold
|
||||
if options["player_hotstart"]:
|
||||
start_type = dcs.mission.StartType.Warm
|
||||
|
||||
# Adds vehicles as a single group (for easy late activation), and helicopters if enabled in settings
|
||||
# def addZoneFARP(_zone_name, country, file):
|
||||
#
|
||||
# farp_flag = self.m.find_group(_zone_name)
|
||||
#
|
||||
# if farp_flag:
|
||||
# farp_position = farp_flag.units[0].position
|
||||
# farp_heading = farp_flag.units[0].heading
|
||||
# else:
|
||||
# farp_position = self.all_zones[_zone_name].position
|
||||
# farp_heading = 0
|
||||
#
|
||||
# # Add the basic invisible farp object
|
||||
# farp = self.m.farp(self.m.country(country), _zone_name + " FARP", farp_position,
|
||||
# hidden=False, dead=False,
|
||||
# farp_type=dcs.unit.InvisibleFARP)
|
||||
#
|
||||
# # Use alternate template file if it has been defined in scenario config
|
||||
# if options["zone_farp_file"]:
|
||||
#
|
||||
# for i in imports:
|
||||
# if i.filename.removesuffix('.miz') == options["zone_farp_file"]:
|
||||
# file = i.path
|
||||
# # if multiple files found, we want the latest file to override the first
|
||||
#
|
||||
# i = ImportObjects(file)
|
||||
# i.anchorByGroupName("ANCHOR")
|
||||
# farp_group = i.copyVehiclesAsGroup(self.m, country, _zone_name + " FARP Static", farp_position,
|
||||
# farp_heading)
|
||||
# # Add client helicopters
|
||||
# if options["farp_spawns"]:
|
||||
# helicopter_groups = i.copyHelicopters(self.m, jtf_blue, "ZONE " + _zone_name + " EMPTY ", farp_position, farp_heading)
|
||||
# for group in helicopter_groups:
|
||||
# self.all_zones[_zone_name].player_helo_spawns.append(group)
|
||||
#
|
||||
# return farp_group
|
||||
|
||||
# # Adds statics, vehicles, and helicopters. Late activation is not possible
|
||||
# def addLogisticsZone(_zone_name, country, file, config_name, helicopters=False):
|
||||
# flag = self.m.find_group(_zone_name)
|
||||
# if flag:
|
||||
# position = flag.units[0].position
|
||||
# heading = flag.units[0].heading
|
||||
# else:
|
||||
# position = self.all_zones[_zone_name].position
|
||||
# heading = 0
|
||||
#
|
||||
# # Use alternate template file if it has been defined in scenario config
|
||||
# if options[config_name]:
|
||||
#
|
||||
# for i in imports:
|
||||
# if i.filename.removesuffix('.miz') == options[config_name]:
|
||||
# file = i.path
|
||||
# # if multiple files found, we want the latest file to override the first
|
||||
#
|
||||
# # Import statics and vehicles
|
||||
# i = ImportObjects(file)
|
||||
# i.anchorByGroupName("ANCHOR")
|
||||
# i.copyStatics(self.m, country, _zone_name + " Logistics Zone",
|
||||
# position, heading)
|
||||
# i.copyVehicles(self.m, country, _zone_name + " Logistics Zone",
|
||||
# position, heading)
|
||||
#
|
||||
# # Add client helicopters
|
||||
# if helicopters:
|
||||
# helicopter_groups = i.copyHelicopters(self.m, jtf_blue, "ZONE " + _zone_name + " EMPTY ", position,
|
||||
# heading)
|
||||
# for group in helicopter_groups:
|
||||
# self.all_zones[_zone_name].player_helo_spawns.append(group)
|
||||
|
||||
# Adds statics, vehicles, and helicopters (if enabled). Late activation is not possible.
|
||||
# def addDefensiveFARP(_zone_name, country, file):
|
||||
#
|
||||
# farp_flag = self.m.find_group(_zone_name)
|
||||
#
|
||||
# if farp_flag:
|
||||
# farp_position = farp_flag.units[0].position
|
||||
# farp_heading = farp_flag.units[0].heading
|
||||
# else:
|
||||
# farp_position = self.all_zones[_zone_name].position
|
||||
# farp_heading = 0
|
||||
#
|
||||
# # Add the basic invisible farp object
|
||||
# farp = self.m.farp(self.m.country(country), _zone_name + " FARP", farp_position,
|
||||
# hidden=False, dead=False,
|
||||
# farp_type=dcs.unit.InvisibleFARP)
|
||||
#
|
||||
# # Use alternate template file if it has been defined in scenario config
|
||||
# if options["defensive_farp_file"]:
|
||||
#
|
||||
# for i in imports:
|
||||
# if i.filename.removesuffix('.miz') == options["defensive_farp_file"]:
|
||||
# file = i.path
|
||||
# # if multiple files found, we want the latest file to override the first
|
||||
#
|
||||
# # Import statics and vehicles
|
||||
# i = ImportObjects(file)
|
||||
# i.anchorByGroupName("ANCHOR")
|
||||
# i.copyStatics(self.m, country, _zone_name + " Logistics Zone",
|
||||
# farp_position, farp_heading)
|
||||
# i.copyVehicles(self.m, country, _zone_name + " Logistics Zone",
|
||||
# farp_position, farp_heading)
|
||||
#
|
||||
# # Import player helicopters
|
||||
# if options["farp_spawns"]:
|
||||
# helicopter_groups = i.copyHelicopters(self.m, jtf_blue, "ZONE " + _zone_name + " EMPTY ", farp_position,
|
||||
# farp_heading)
|
||||
# for group in helicopter_groups:
|
||||
# self.all_zones[_zone_name].player_helo_spawns.append(group)
|
||||
|
||||
for zone_name in red_zones:
|
||||
if red_forces["vehicles"]:
|
||||
self.addGroundGroups(red_zones[zone_name], self.m.country(jtf_red), red_forces["vehicles"], options["red_quantity"])
|
||||
|
||||
#Add red FARPS
|
||||
self.addGroundGroups(red_zones[zone_name], self.m.country(jtf_red), red_forces["vehicles"],
|
||||
options["red_quantity"])
|
||||
|
||||
if options["zone_farps"] != "farp_never" and not options["defending"]:
|
||||
# RotorOpsGroups.VehicleTemplate.CombinedJointTaskForcesBlue.zone_farp(self.m, self.m.country(jtf_blue),
|
||||
# self.m.country(jtf_blue),
|
||||
# red_zones[zone_name].position,
|
||||
# 180, zone_name + " FARP", late_activation=True)
|
||||
helicopters = False
|
||||
if options["farp_spawns"]:
|
||||
helicopters = True
|
||||
|
||||
#new_statics, new_vehicles, new_helicopters = i.copyAll(self.m, dcs.countries.UnitedNationsPeacekeepers.name, zone_name, red_zones[zone_name].position)
|
||||
# Add red zone FARPS
|
||||
|
||||
farp_flag = self.m.find_group(zone_name)
|
||||
|
||||
if farp_flag:
|
||||
farp_position = farp_flag.units[0].position
|
||||
farp_heading = farp_flag.units[0].heading
|
||||
else:
|
||||
farp_position = red_zones[zone_name].position
|
||||
farp_heading = 0
|
||||
|
||||
farp = self.m.farp(self.m.country(jtf_blue), zone_name + " FARP", farp_position,
|
||||
hidden=False, dead=False,
|
||||
farp_type=dcs.unit.InvisibleFARP)
|
||||
|
||||
os.chdir(directories.imports)
|
||||
if self.config and self.config["zone_farp_file"]:
|
||||
filename = self.config["zone_farp_file"]
|
||||
else:
|
||||
filename = "FARP_DEFAULT_ZONE.miz"
|
||||
i = ImportObjects(filename)
|
||||
i.anchorByGroupName("ANCHOR")
|
||||
farp_group = i.copyVehiclesAsGroup(self.m, jtf_blue, zone_name + " FARP Static", farp_position, farp_heading)
|
||||
farp_group.late_activation = True
|
||||
|
||||
|
||||
|
||||
if options["zone_protect_sams"]:
|
||||
self.m.vehicle_group(
|
||||
self.m.country(jtf_red),
|
||||
"Static " + zone_name + " Protection SAM",
|
||||
random.choice(RotorOpsUnits.e_zone_sams),
|
||||
red_zones[zone_name].position,
|
||||
heading=random.randint(0, 359),
|
||||
group_size=6,
|
||||
formation=dcs.unitgroup.VehicleGroup.Formation.Star
|
||||
vehicle_group = self.addZoneBase(options, zone_name, jtf_blue,
|
||||
file=activated_farp,
|
||||
config_name="zone_farp_file",
|
||||
copy_helicopters=helicopters,
|
||||
helicopters_name="ZONE " + zone_name + " EMPTY",
|
||||
heli_start_type=dcs.mission.StartType.Cold,
|
||||
copy_vehicles=True,
|
||||
vehicles_name=zone_name + " FARP Static",
|
||||
copy_statics=False,
|
||||
statics_names="",
|
||||
vehicles_single_group=True,
|
||||
trigger_name=zone_name + "_FARP",
|
||||
trigger_radius=110
|
||||
)
|
||||
vehicle_group.late_activation = True
|
||||
|
||||
|
||||
# For SAMs: Add vehicles as a single group (for easy late activation)
|
||||
if options["zone_protect_sams"]:
|
||||
sam_group = self.addZoneBase(options, zone_name, jtf_red,
|
||||
file=zone_protect,
|
||||
config_name="zone_protect_file",
|
||||
copy_vehicles=True,
|
||||
vehicles_name=zone_name + " Protect Static",
|
||||
vehicles_single_group=True
|
||||
)
|
||||
# farp_flag = self.m.find_group(zone_name)
|
||||
#
|
||||
# if farp_flag:
|
||||
# farp_position = farp_flag.units[0].position
|
||||
# farp_heading = farp_flag.units[0].heading
|
||||
# else:
|
||||
# farp_position = self.all_zones[zone_name].position
|
||||
# farp_heading = 0
|
||||
#
|
||||
# i = ImportObjects(zone_protect)
|
||||
# i.anchorByGroupName("ANCHOR")
|
||||
# farp_group = i.copyVehiclesAsGroup(self.m, jtf_red, "Static " + zone_name + " Protection SAM",
|
||||
# farp_position,
|
||||
# farp_heading)
|
||||
|
||||
# Populate Blue zones with ground units
|
||||
for zone_name in blue_zones:
|
||||
for i, zone_name in enumerate(blue_zones):
|
||||
if blue_forces["vehicles"]:
|
||||
self.addGroundGroups(blue_zones[zone_name], self.m.country(jtf_blue), blue_forces["vehicles"],
|
||||
options["blue_quantity"])
|
||||
#Add blue FARPS
|
||||
|
||||
# Add blue zone FARPS (not late activated) for defensive mode
|
||||
if options["zone_farps"] != "farp_never" and options["defending"]:
|
||||
RotorOpsGroups.VehicleTemplate.CombinedJointTaskForcesBlue.zone_farp(self.m, self.m.country(jtf_blue),
|
||||
self.m.country(jtf_blue),
|
||||
blue_zones[zone_name].position,
|
||||
180, zone_name + " FARP", late_activation=False)
|
||||
|
||||
#add logistics sites
|
||||
if options["crates"] and zone_name == "STAGING":
|
||||
os.chdir(directories.imports)
|
||||
staging_flag = self.m.find_group(zone_name)
|
||||
if staging_flag:
|
||||
staging_position = staging_flag.units[0].position
|
||||
staging_heading = staging_flag.units[0].heading
|
||||
helicopters = False
|
||||
if options["farp_spawns"]:
|
||||
helicopters = True
|
||||
|
||||
if options["crates"] and i == len(blue_zones) - 1:
|
||||
# add a logistics zone to the last conflict zone
|
||||
# addLogisticsZone(zone_name, jtf_blue, logistics_farp, "logistics_farp_file", helicopters)
|
||||
self.addZoneBase(options, zone_name, jtf_blue,
|
||||
file=logistics_farp,
|
||||
config_name="logistics_farp_file",
|
||||
copy_helicopters=helicopters,
|
||||
helicopters_name="ZONE " + zone_name + " LOGISTICS",
|
||||
heli_start_type=start_type,
|
||||
copy_vehicles=True,
|
||||
vehicles_name=zone_name + " Logistics FARP",
|
||||
copy_statics=True,
|
||||
statics_names=zone_name + " Logistics FARP",
|
||||
vehicles_single_group=False,
|
||||
trigger_name=zone_name + "_FARP",
|
||||
trigger_radius=110
|
||||
)
|
||||
else:
|
||||
staging_position = blue_zones[zone_name].position
|
||||
staging_heading = 0
|
||||
i = ImportObjects("STAGING_LOGISTIC_HUB.miz")
|
||||
i.anchorByGroupName("ANCHOR")
|
||||
i.copyAll(self.m, jtf_blue, "Staging Logistics Zone",
|
||||
staging_position, staging_heading)
|
||||
|
||||
if options["zone_protect_sams"] and options["defending"]:
|
||||
vg = self.m.vehicle_group(
|
||||
self.m.country(jtf_blue),
|
||||
"Static " + zone_name + " Protection SAM",
|
||||
random.choice(RotorOpsUnits.e_zone_sams),
|
||||
blue_zones[zone_name].position,
|
||||
heading=random.randint(0, 359),
|
||||
group_size=6,
|
||||
formation=dcs.unitgroup.VehicleGroup.Formation.Star
|
||||
# addDefensiveFARP(zone_name, jtf_blue, defensive_farp)
|
||||
self.addZoneBase(options, zone_name, jtf_blue,
|
||||
file=defensive_farp,
|
||||
config_name="defensive_farp_file",
|
||||
copy_helicopters=helicopters,
|
||||
helicopters_name="ZONE " + zone_name + " EMPTY",
|
||||
heli_start_type=dcs.mission.StartType.Cold,
|
||||
copy_vehicles=True,
|
||||
vehicles_name=zone_name + " Defensive FARP",
|
||||
copy_statics=True,
|
||||
statics_names=zone_name + " Defensive FARP",
|
||||
vehicles_single_group=False,
|
||||
trigger_name=zone_name + "_FARP",
|
||||
trigger_radius=110
|
||||
)
|
||||
|
||||
# add main logistics base
|
||||
if options["crates"] and zone_name == "STAGING":
|
||||
# addLogisticsZone(zone_name, jtf_blue, logistics_base, "staging_logistics_file", helicopters=True)
|
||||
self.addZoneBase(options, zone_name, jtf_blue,
|
||||
file=logistics_base,
|
||||
config_name="staging_logistics_file",
|
||||
copy_helicopters=True,
|
||||
helicopters_name="ZONE " + zone_name + " LOGISTICS",
|
||||
heli_start_type=start_type,
|
||||
copy_vehicles=True,
|
||||
vehicles_name=zone_name + " Logistics Base",
|
||||
copy_statics=True,
|
||||
statics_names=zone_name + " Logistics Base",
|
||||
vehicles_single_group=False,
|
||||
trigger_name="STAGING_BASE",
|
||||
trigger_radius=170
|
||||
)
|
||||
|
||||
# Add player slots
|
||||
window.statusBar().showMessage("Adding flights to mission...", 10000)
|
||||
@ -293,6 +459,34 @@ class RotorOpsMission:
|
||||
self.addResources(directories.sound, directories.scripts)
|
||||
RotorOpsConflict.triggerSetup(self, options)
|
||||
|
||||
# finalize the mission briefing
|
||||
briefing = self.m.description_text() + '## RotorOps Credits ##\n\n' + options["credits"]
|
||||
briefing = briefing + "\nFor more info on RotorOps, visit: DCS-HELICOPTERS.COM"
|
||||
self.m.set_description_text(briefing)
|
||||
|
||||
# set the weather and time
|
||||
|
||||
if options["random_weather"]:
|
||||
# self.m.random_weather = True
|
||||
max = len(dcs.cloud_presets.CLOUD_PRESETS) - 1
|
||||
preset_name = list(dcs.cloud_presets.CLOUD_PRESETS)[random.randint(0, max)]
|
||||
cloud_preset = dcs.weather.CloudPreset.by_name(preset_name)
|
||||
self.m.weather.clouds_base = random.randrange(cloud_preset.min_base, cloud_preset.max_base)
|
||||
self.m.weather.clouds_preset = cloud_preset
|
||||
wind_dir = random.randrange(0, 359) + 180
|
||||
wind_speed = random.randrange(5, 10)
|
||||
self.m.weather.wind_at_ground.direction = (wind_dir + random.randrange(-90, 90) - 180) % 360
|
||||
self.m.weather.wind_at_ground.speed = wind_speed + random.randrange(-4, -1)
|
||||
self.m.weather.wind_at_2000.direction = (wind_dir + random.randrange(-90, 90) - 180) % 360
|
||||
self.m.weather.wind_at_2000.speed = wind_speed + random.randrange(-2, 2)
|
||||
self.m.weather.wind_at_8000.direction = (wind_dir + random.randrange(-90, 90) - 180) % 360
|
||||
self.m.weather.wind_at_8000.speed = wind_speed + random.randrange(-1, 10)
|
||||
|
||||
logger.info("Cloud preset = " + cloud_preset.ui_name + ", ground windspeed = " + str(
|
||||
self.m.weather.wind_at_ground.speed))
|
||||
|
||||
if options["time"] != "Default Time":
|
||||
self.m.random_daytime(options["time"].lower())
|
||||
|
||||
# Save the mission file
|
||||
window.statusBar().showMessage("Saving mission...", 10000)
|
||||
@ -305,6 +499,63 @@ class RotorOpsMission:
|
||||
success = self.m.save(output_filename)
|
||||
return {"success": success, "filename": output_filename, "directory": output_dir} # let the UI know the result
|
||||
|
||||
# Use the ImportObjects class to place farps and bases
|
||||
def addZoneBase(self, options, _zone_name, country, file, config_name=None, copy_helicopters=False,
|
||||
helicopters_name="", heli_start_type=dcs.mission.StartType.Cold,
|
||||
copy_vehicles=False, vehicles_name="", copy_statics=False, statics_names="",
|
||||
vehicles_single_group=False, trigger_name=None, trigger_radius=110, farp=True):
|
||||
|
||||
# look for a marker object to position the base at a position other than zone center
|
||||
flag = self.m.find_group(_zone_name)
|
||||
if flag:
|
||||
position = flag.units[0].position
|
||||
heading = flag.units[0].heading
|
||||
self.all_zones[_zone_name].base_position = position
|
||||
else:
|
||||
position = self.all_zones[_zone_name].position
|
||||
heading = 0
|
||||
|
||||
if farp:
|
||||
farp = self.m.farp(self.m.country(country), _zone_name + " FARP",
|
||||
position, hidden=True, dead=False, farp_type=dcs.unit.InvisibleFARP)
|
||||
|
||||
# Add a trigger zone
|
||||
if trigger_name:
|
||||
self.m.triggers.add_triggerzone(position, trigger_radius, False, trigger_name)
|
||||
|
||||
# Use alternate template file if it has been defined in scenario config
|
||||
if config_name and options[config_name]:
|
||||
|
||||
for i in self.imports:
|
||||
if i.filename.removesuffix('.miz') == options[config_name]:
|
||||
file = i.path
|
||||
# if multiple files found, we want the latest file to override the first
|
||||
|
||||
# Import statics and vehicles
|
||||
i = ImportObjects(file)
|
||||
i.anchorByGroupName("ANCHOR")
|
||||
|
||||
if copy_statics:
|
||||
i.copyStatics(self.m, country, statics_names,
|
||||
position, heading)
|
||||
vehicle_group = None
|
||||
if copy_vehicles:
|
||||
if vehicles_single_group:
|
||||
vehicle_group = i.copyVehiclesAsGroup(self.m, country, vehicles_name, position,
|
||||
heading)
|
||||
else:
|
||||
i.copyVehicles(self.m, country, vehicles_name,
|
||||
position, heading)
|
||||
|
||||
# Add client helicopters and farp objects
|
||||
if copy_helicopters:
|
||||
helicopter_groups = i.copyHelicopters(self.m, jtf_blue, helicopters_name, position,
|
||||
heading, heli_start_type)
|
||||
for group in helicopter_groups:
|
||||
self.all_zones[_zone_name].player_helo_spawns.append(group)
|
||||
|
||||
return vehicle_group # for setting properties such as late activation
|
||||
|
||||
def addGroundGroups(self, zone, _country, groups, quantity):
|
||||
for a in range(0, quantity):
|
||||
|
||||
@ -318,12 +569,11 @@ class RotorOpsMission:
|
||||
country,
|
||||
zone.name + '-GND ' + str(a + 1),
|
||||
unit_types,
|
||||
zone.position.random_point_within(zone.size / 1.2, 100),
|
||||
zone.position.random_point_within(zone.size / 1.3, 100),
|
||||
heading=random.randint(0, 359),
|
||||
formation=dcs.unitgroup.VehicleGroup.Formation.Scattered,
|
||||
)
|
||||
|
||||
|
||||
def getCoalitionAirports(self, side: str):
|
||||
coalition_airports = []
|
||||
primary_airport = None
|
||||
@ -335,7 +585,8 @@ class RotorOpsMission:
|
||||
coalition_airports.append(airportobj)
|
||||
|
||||
start = self.staging_zones[list(self.staging_zones)[0]]
|
||||
dist_from_start = dcs.mapping._distance(airportobj.position.x, airportobj.position.y, start.position.x, start.position.y)
|
||||
dist_from_start = dcs.mapping._distance(airportobj.position.x, airportobj.position.y, start.position.x,
|
||||
start.position.y)
|
||||
|
||||
if dist_from_start < shortest_dist:
|
||||
primary_airport = airportobj
|
||||
@ -346,10 +597,10 @@ class RotorOpsMission:
|
||||
def getParking(self, airport, aircraft, alt_airports=None, group_size=1):
|
||||
|
||||
if len(airport.free_parking_slots(aircraft)) >= group_size:
|
||||
if not (aircraft.id in dcs.planes.plane_map and (len(airport.runways) == 0 or airport.runways[0].ils is None)):
|
||||
if not (aircraft.id in dcs.planes.plane_map and (
|
||||
len(airport.runways) == 0 or airport.runways[0].ils is None)):
|
||||
return airport
|
||||
|
||||
|
||||
if alt_airports:
|
||||
for airport in alt_airports:
|
||||
if len(airport.free_parking_slots(aircraft)) >= group_size:
|
||||
@ -363,7 +614,6 @@ class RotorOpsMission:
|
||||
def getUnitParking(self, aircraft):
|
||||
return
|
||||
|
||||
|
||||
def swapSides(self, options):
|
||||
|
||||
# Swap airports
|
||||
@ -379,7 +629,6 @@ class RotorOpsMission:
|
||||
combinedJointTaskForcesBlue = self.m.country(jtf_blue)
|
||||
combinedJointTaskForcesRed = self.m.country(jtf_red)
|
||||
|
||||
|
||||
# Swap ships
|
||||
|
||||
blue_ships = combinedJointTaskForcesBlue.ship_group.copy()
|
||||
@ -390,13 +639,10 @@ class RotorOpsMission:
|
||||
combinedJointTaskForcesRed.add_ship_group(group)
|
||||
combinedJointTaskForcesBlue.ship_group.remove(group)
|
||||
|
||||
|
||||
for group in red_ships:
|
||||
combinedJointTaskForcesBlue.add_ship_group(group)
|
||||
combinedJointTaskForcesRed.ship_group.remove(group)
|
||||
|
||||
|
||||
|
||||
# Swap statics
|
||||
|
||||
blue_statics = combinedJointTaskForcesBlue.static_group.copy()
|
||||
@ -410,7 +656,6 @@ class RotorOpsMission:
|
||||
combinedJointTaskForcesRed.static_group.remove(group)
|
||||
combinedJointTaskForcesBlue.add_static_group(group)
|
||||
|
||||
|
||||
# Swap vehicles
|
||||
|
||||
blue_vehicles = combinedJointTaskForcesBlue.vehicle_group.copy()
|
||||
@ -424,7 +669,6 @@ class RotorOpsMission:
|
||||
combinedJointTaskForcesRed.vehicle_group.remove(group)
|
||||
combinedJointTaskForcesBlue.add_vehicle_group(group)
|
||||
|
||||
|
||||
# Swap planes
|
||||
|
||||
blue_planes = combinedJointTaskForcesBlue.plane_group.copy()
|
||||
@ -438,7 +682,6 @@ class RotorOpsMission:
|
||||
combinedJointTaskForcesRed.plane_group.remove(group)
|
||||
combinedJointTaskForcesBlue.add_plane_group(group)
|
||||
|
||||
|
||||
# Swap helicopters
|
||||
|
||||
blue_helos = combinedJointTaskForcesBlue.helicopter_group.copy()
|
||||
@ -452,12 +695,15 @@ class RotorOpsMission:
|
||||
combinedJointTaskForcesRed.helicopter_group.remove(group)
|
||||
combinedJointTaskForcesBlue.add_helicopter_group(group)
|
||||
|
||||
|
||||
def addPlayerHelos(self, options):
|
||||
client_helos = RotorOpsUnits.client_helos
|
||||
unslotted_count = 0
|
||||
slotted_count = 0
|
||||
|
||||
for helicopter in dcs.helicopters.helicopter_map:
|
||||
if helicopter == options["slots"]:
|
||||
client_helos = [dcs.helicopters.helicopter_map[helicopter]] #if out ui slot option matches a specific helicopter type name
|
||||
client_helos = [dcs.helicopters.helicopter_map[
|
||||
helicopter]] # if out ui slot option matches a specific helicopter type name
|
||||
|
||||
# get loadouts from miz file and put into a simple dict
|
||||
default_loadouts = {}
|
||||
@ -476,10 +722,14 @@ class RotorOpsMission:
|
||||
farp = self.m.country(jtf_blue).find_static_group("HELO_FARP")
|
||||
if not farp:
|
||||
farp = self.m.country(jtf_blue).find_static_group("HELO_FARP_1")
|
||||
heading = 0
|
||||
if farp:
|
||||
farp_heading = farp.units[0].heading
|
||||
heading = farp_heading
|
||||
|
||||
friendly_airports, primary_f_airport = self.getCoalitionAirports("blue")
|
||||
|
||||
heading = 0
|
||||
|
||||
group_size = 1
|
||||
player_helicopters = []
|
||||
if options["slots"] == "Multiple Slots":
|
||||
@ -490,34 +740,75 @@ class RotorOpsMission:
|
||||
if len(client_helos) == 1:
|
||||
group_size = 2 # add a wingman if singleplayer
|
||||
|
||||
# Hot/Cold start options
|
||||
start_type = dcs.mission.StartType.Cold
|
||||
start_type_string = ""
|
||||
start_type_point_type = "TakeOffGround"
|
||||
start_type_action = dcs.point.PointAction.FromGroundArea
|
||||
if options["player_hotstart"]:
|
||||
start_type = dcs.mission.StartType.Warm
|
||||
start_type_string = "HOT "
|
||||
start_type_point_type = "TakeOffGroundHot"
|
||||
start_type_action = dcs.point.PointAction.FromGroundAreaHot
|
||||
|
||||
farp_helicopter_count = 1
|
||||
for helicopter_id in player_helicopters:
|
||||
fg = None
|
||||
helotype = None
|
||||
if helicopter_id in dcs.helicopters.helicopter_map:
|
||||
helotype = dcs.helicopters.helicopter_map[helicopter_id]
|
||||
else:
|
||||
continue
|
||||
if carrier:
|
||||
fg = self.m.flight_group_from_unit(self.m.country(jtf_blue), "CARRIER " + helotype.id, helotype, carrier,
|
||||
fg = self.m.flight_group_from_unit(self.m.country(jtf_blue),
|
||||
"CARRIER " + start_type_string + helotype.id, helotype,
|
||||
carrier,
|
||||
dcs.task.CAS, group_size=group_size, start_type=start_type)
|
||||
|
||||
elif farp and farp_helicopter_count <= 4:
|
||||
farp_helicopter_count = farp_helicopter_count + 1
|
||||
fg = self.m.flight_group_from_unit(self.m.country(jtf_blue), "FARP " + helotype.id, helotype, farp,
|
||||
|
||||
|
||||
#old ugly FARPs, or single player groups with wingman require fg from unit
|
||||
if farp.units[0].type != 'Invisible FARP':
|
||||
print("making flight group from unit")
|
||||
fg = self.m.flight_group_from_unit(self.m.country(jtf_blue),
|
||||
"FARP " + start_type_string + helotype.id, helotype, farp,
|
||||
dcs.task.CAS, group_size=group_size, start_type=start_type)
|
||||
|
||||
# invisible farps need manual unit placement for multiple units
|
||||
if farp.units[0].type == 'Invisible FARP':
|
||||
fg.points[0].action = dcs.point.PointAction.FromGroundArea
|
||||
fg.points[0].type = "TakeOffGround"
|
||||
fg.units[0].position = fg.units[0].position.point_from_heading(heading, 20)
|
||||
elif farp.units[0].type == 'Invisible FARP':
|
||||
print("making standard flight group")
|
||||
pos = farp.units[0].position.point_from_heading(heading, 20)
|
||||
farp_helicopter_count = farp_helicopter_count + 1
|
||||
|
||||
fg = self.m.flight_group(self.m.country(jtf_blue), "FARP " + start_type_string + helotype.id,
|
||||
helotype, airport=None, position=pos, maintask=dcs.task.CAS, group_size=group_size, start_type=start_type)
|
||||
|
||||
fg.units[0].heading = farp_heading
|
||||
|
||||
if group_size > 1:
|
||||
# move wingman if present
|
||||
fg.units[1].position = farp.units[0].position.point_from_heading(180, 20)
|
||||
fg.units[1].heading = farp_heading
|
||||
|
||||
# change heading for next helicopter placement
|
||||
heading += 90
|
||||
|
||||
# hot or cold start
|
||||
fg.points[0].action = start_type_action
|
||||
fg.points[0].type = start_type_point_type
|
||||
else:
|
||||
fg = self.m.flight_group_from_airport(self.m.country(jtf_blue), primary_f_airport.name + " " + helotype.id, helotype,
|
||||
self.getParking(primary_f_airport, helotype), group_size=group_size, start_type=start_type)
|
||||
parking = self.getParking(primary_f_airport, helotype, friendly_airports,
|
||||
group_size=group_size)
|
||||
if parking:
|
||||
fg = self.m.flight_group_from_airport(self.m.country(jtf_blue),
|
||||
primary_f_airport.name + " " + start_type_string + helotype.id,
|
||||
helotype,
|
||||
parking, group_size=group_size, start_type=start_type)
|
||||
|
||||
# if we were able to find a slot and create a flight group
|
||||
if fg:
|
||||
slotted_count = slotted_count + 1
|
||||
fg.units[0].set_client()
|
||||
# fg.load_task_default_loadout(dcs.task.CAS)
|
||||
if helotype.id in default_loadouts:
|
||||
@ -531,7 +822,13 @@ class RotorOpsMission:
|
||||
fg.units[1].pylons = fg.units[0].pylons
|
||||
fg.units[1].livery_id = fg.units[0].livery_id
|
||||
fg.units[1].fuel = fg.units[0].fuel
|
||||
else:
|
||||
logger.warn("No parking available for " + helotype.id)
|
||||
unslotted_count = unslotted_count + 1
|
||||
|
||||
if unslotted_count > 0:
|
||||
raise Exception("Player slots error: Unable to find parking for " + str(
|
||||
unslotted_count) + " players. Maximum parking slots found was " + str(slotted_count))
|
||||
|
||||
class TrainingScenario():
|
||||
@staticmethod
|
||||
@ -548,8 +845,10 @@ class RotorOpsMission:
|
||||
def perpRacetrack(enemy_heading, friendly_pt, terrain):
|
||||
heading = enemy_heading + random.randrange(70, 110)
|
||||
race_dist = random.randrange(40 * 1000, 80 * 1000)
|
||||
center_pt = dcs.mapping.point_from_heading(friendly_pt.x, friendly_pt.y, enemy_heading - random.randrange(140, 220), 10000)
|
||||
pt1 = dcs.mapping.point_from_heading(center_pt[0], center_pt[1], enemy_heading - 90, random.randrange(20 * 1000, 40 * 1000))
|
||||
center_pt = dcs.mapping.point_from_heading(friendly_pt.x, friendly_pt.y,
|
||||
enemy_heading - random.randrange(140, 220), 10000)
|
||||
pt1 = dcs.mapping.point_from_heading(center_pt[0], center_pt[1], enemy_heading - 90,
|
||||
random.randrange(20 * 1000, 40 * 1000))
|
||||
return dcs.mapping.Point(pt1[0], pt1[1], terrain), heading, race_dist
|
||||
|
||||
def addFlights(self, options, red_forces, blue_forces):
|
||||
@ -568,19 +867,21 @@ class RotorOpsMission:
|
||||
farp = self.m.country(jtf_red).find_static_group("HELO_FARP_1")
|
||||
|
||||
e_airport_heading = dcs.mapping.heading_between_points(
|
||||
friendly_airports[0].position.x, friendly_airports[0].position.y, enemy_airports[0].position.x, primary_e_airport.position.y
|
||||
friendly_airports[0].position.x, friendly_airports[0].position.y, enemy_airports[0].position.x,
|
||||
primary_e_airport.position.y
|
||||
)
|
||||
|
||||
e_airport_distance = dcs.mapping._distance(
|
||||
primary_f_airport.position.x, primary_f_airport.position.y, primary_f_airport.position.x, primary_f_airport.position.y
|
||||
primary_f_airport.position.x, primary_f_airport.position.y, primary_f_airport.position.x,
|
||||
primary_f_airport.position.y
|
||||
)
|
||||
|
||||
|
||||
if options["f_awacs"]:
|
||||
awacs_name = "AWACS"
|
||||
awacs_freq = 266
|
||||
# pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
|
||||
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position, self.m.terrain)
|
||||
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position,
|
||||
self.m.terrain)
|
||||
awacs = self.m.awacs_flight(
|
||||
combinedJointTaskForcesBlue,
|
||||
awacs_name,
|
||||
@ -613,7 +914,6 @@ class RotorOpsMission:
|
||||
unit.pylons = source_plane.pylons
|
||||
unit.livery_id = source_plane.livery_id
|
||||
|
||||
|
||||
# add text to mission briefing with radio freq
|
||||
briefing = self.m.description_text() + "\n\n" + awacs_name + " " + str(awacs_freq) + ".00 " + "\n"
|
||||
self.m.set_description_text(briefing)
|
||||
@ -626,7 +926,8 @@ class RotorOpsMission:
|
||||
t2_freq = 256
|
||||
t2_tac = "101Y"
|
||||
# pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
|
||||
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position, self.m.terrain)
|
||||
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position,
|
||||
self.m.terrain)
|
||||
refuel_net = self.m.refuel_flight(
|
||||
combinedJointTaskForcesBlue,
|
||||
t1_name,
|
||||
@ -642,7 +943,8 @@ class RotorOpsMission:
|
||||
tacanchannel=t1_tac)
|
||||
|
||||
# pos, heading, race_dist = self.TrainingScenario.random_orbit(orbit_rect)
|
||||
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position, self.m.terrain)
|
||||
pos, heading, race_dist = self.TrainingScenario.perpRacetrack(e_airport_heading, primary_f_airport.position,
|
||||
self.m.terrain)
|
||||
refuel_rod = self.m.refuel_flight(
|
||||
combinedJointTaskForcesBlue,
|
||||
t2_name,
|
||||
@ -656,7 +958,8 @@ class RotorOpsMission:
|
||||
tacanchannel=t2_tac)
|
||||
|
||||
# add text to mission briefing
|
||||
briefing = self.m.description_text() + "\n\n" + t1_name + " " + str(t1_freq) + ".00 " + t1_tac + "\n" + t2_name + " " + str(t2_freq) + ".00 " + t2_tac + "\n"
|
||||
briefing = self.m.description_text() + "\n\n" + t1_name + " " + str(
|
||||
t1_freq) + ".00 " + t1_tac + "\n" + t2_name + " " + str(t2_freq) + ".00 " + t2_tac + "\n\n"
|
||||
self.m.set_description_text(briefing)
|
||||
|
||||
def zone_attack(fg, airport):
|
||||
@ -676,9 +979,6 @@ class RotorOpsMission:
|
||||
fg.points[0].tasks.append(dcs.task.OptReactOnThreat(dcs.task.OptReactOnThreat.Values.EvadeFire))
|
||||
fg.points[0].tasks.append(dcs.task.OptROE(dcs.task.OptROE.Values.OpenFire))
|
||||
|
||||
|
||||
|
||||
|
||||
if options["e_attack_helos"]:
|
||||
source_helo = None
|
||||
if red_forces["attack_helos"]:
|
||||
@ -736,8 +1036,6 @@ class RotorOpsMission:
|
||||
unit.pylons = source_helo.pylons
|
||||
unit.livery_id = source_helo.livery_id
|
||||
|
||||
|
||||
|
||||
if options["e_attack_planes"]:
|
||||
source_plane = None
|
||||
if red_forces["attack_planes"]:
|
||||
@ -795,12 +1093,9 @@ class RotorOpsMission:
|
||||
unit.pylons = source_helo.pylons
|
||||
unit.livery_id = source_helo.livery_id
|
||||
|
||||
def importObjects(self, data):
|
||||
|
||||
|
||||
|
||||
def importObjects(self):
|
||||
os.chdir(directories.imports)
|
||||
logger.info("Looking for import .miz files in '" + os.getcwd())
|
||||
imports = data["objects"]["imports"]
|
||||
|
||||
for side in "red", "blue", "neutrals":
|
||||
coalition = self.m.coalition.get(side)
|
||||
@ -810,8 +1105,10 @@ class RotorOpsMission:
|
||||
if group.name.find(prefix) == 0:
|
||||
if group.units[0].name.find('IMPORT-') == 0:
|
||||
logger.error(
|
||||
group.units[0].name + " IMPORT group's unit name cannot start with 'IMPORT'. Check the scenario template.")
|
||||
raise Exception("Scenario file error: " + group.units[0].name + " IMPORT group's unit name cannot start with 'IMPORT'")
|
||||
group.units[
|
||||
0].name + " IMPORT group's unit name cannot start with 'IMPORT'. Check the scenario template.")
|
||||
raise Exception("Scenario file error: " + group.units[
|
||||
0].name + " IMPORT group's unit name cannot start with 'IMPORT'")
|
||||
|
||||
# trim the groupname to our filename convention
|
||||
filename = group.name.removeprefix(prefix)
|
||||
@ -820,10 +1117,16 @@ class RotorOpsMission:
|
||||
filename = filename[0:i]
|
||||
print(filename)
|
||||
|
||||
filename = filename + ".miz"
|
||||
i = ImportObjects(filename)
|
||||
for imp in imports:
|
||||
if imp.filename == (filename + ".miz"):
|
||||
i = ImportObjects(imp.path)
|
||||
i.anchorByGroupName("ANCHOR")
|
||||
new_statics, new_vehicles, new_helicopters = i.copyAll(self.m, country_name, group.units[0].name, group.units[0].position, group.units[0].heading)
|
||||
new_statics, new_vehicles, new_helicopters = i.copyAll(self.m, country_name,
|
||||
group.units[0].name,
|
||||
group.units[0].position,
|
||||
group.units[0].heading)
|
||||
|
||||
break
|
||||
|
||||
def addMods(self):
|
||||
dcs.helicopters.helicopter_map["UH-60L"] = aircraftMods.UH_60L
|
||||
|
||||
Binary file not shown.
11
Generator/tests.py
Normal file
11
Generator/tests.py
Normal file
@ -0,0 +1,11 @@
|
||||
import dcs
|
||||
import dcs.cloud_presets
|
||||
|
||||
testm = dcs.mission.Mission()
|
||||
|
||||
# testCloudPresets
|
||||
for i in range(0, len(dcs.cloud_presets.CLOUD_PRESETS)):
|
||||
preset_name = list(dcs.cloud_presets.CLOUD_PRESETS)[i]
|
||||
cloud_preset = dcs.weather.CloudPreset.by_name(preset_name)
|
||||
testm.weather.clouds_preset = cloud_preset
|
||||
print("Cloud preset = " + cloud_preset.ui_name)
|
||||
3
MissionOutput/.gitignore
vendored
3
MissionOutput/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
*
|
||||
*/
|
||||
!.gitignore
|
||||
@ -21,7 +21,7 @@ spinboxes:
|
||||
e_attack_planes_spinBox: 1
|
||||
e_transport_helos_spinBox: 1
|
||||
troop_drop_spinBox: 4
|
||||
inf_spawn_spinBox: 2
|
||||
inf_spawn_spinBox: 0
|
||||
radiobuttons: farp_gunits,
|
||||
red_forces: "RED Default Armor (HARD)"
|
||||
blue_forces: "BLUE Default US Armor"
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
local_ratings:
|
||||
C:\RotorOps\templates\Scenarios\included\007d3d\Nevada Conflict - Vegas Tour (GRIMM).miz: 4
|
||||
6427
scripts/CTLD2.lua
6427
scripts/CTLD2.lua
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
RotorOps = {}
|
||||
RotorOps.version = "1.2.8"
|
||||
RotorOps.version = "1.3.0"
|
||||
local debug = true
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ local debug = true
|
||||
--- Protip: change these options from the mission editor rather than changing the script file itself. See documentation on github for details.
|
||||
|
||||
|
||||
--RotorOps settings that are safe to change dynamically (ideally from the mission editor in DO SCRIPT for portability). You can change these while the script is running, at any time.
|
||||
--RotorOps settings that can be changed dynamically (ideally from the mission editor in DO SCRIPT for portability). You can change these while the script is running, at any time. Be sure of your syntax and test...errors may crash the script.
|
||||
RotorOps.voice_overs = true
|
||||
RotorOps.ground_speed = 60 --max speed for ground vehicles moving between zones. Doesn't have much effect since always limited by slowest vehicle in group
|
||||
RotorOps.zone_status_display = true --constantly show units remaining and zone status on screen
|
||||
@ -21,8 +21,14 @@ RotorOps.defending_vehicles_disperse = true
|
||||
RotorOps.inf_spawns_avail = 0 --this is the number of infantry group spawn events remaining in the active zone
|
||||
RotorOps.inf_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_spawns_per_zone = 3 --number of infantry groups to spawn per zone
|
||||
RotorOps.inf_spawn_messages = true --voiceovers and messages for infantry spawns
|
||||
RotorOps.inf_spawn_blue = {mg=1,at=0,aa=0,inf=4,mortar=0} --can be an integer quantity, or a ctld defined group table
|
||||
RotorOps.inf_spawn_red = {mg=1,at=0,aa=0,inf=4,mortar=0} --can be an integer quantity, or a ctld defined group table
|
||||
RotorOps.inf_apc_group = {mg=1,at=0,aa=0,inf=3,mortar=0} --can be an integer quantity, or a ctld defined group table
|
||||
RotorOps.inf_spawns_total = 0 --number of infantry groups to spawn per game
|
||||
|
||||
RotorOps.farp_smoke_color = 2 -- Green=0 Red=1 White=2 Orange=3 Blue=4 NONE= -1
|
||||
|
||||
|
||||
--RotorOps settings that are safe to change only before calling setupConflict()
|
||||
@ -31,8 +37,18 @@ RotorOps.CTLD_crates = false
|
||||
RotorOps.CTLD_sound_effects = true --sound effects for troop pickup/dropoffs
|
||||
RotorOps.exclude_ai_group_name = "Static" --include this somewhere in a group name to exclude the group from being tasked in the active zone
|
||||
RotorOps.pickup_zone_smoke = "blue"
|
||||
RotorOps.apc_group = {mg=1,at=0,aa=0,inf=3,mortar=0} --not used yet, but we should define the CTLD groups
|
||||
|
||||
RotorOps.ai_task_by_name = true --allow tasking all groups that include key strings in their group names eg 'Patrol'
|
||||
RotorOps.ai_task_by_name_scheduler = true --continually search active groups for key strings and ai tasking
|
||||
RotorOps.patrol_task_string = 'patrol' --default string to search group names for the patrol task. requires ai_task_by_name
|
||||
RotorOps.aggressive_task_string = 'aggressive' --default string to search group names for the patrol task. requires ai_task_by_name
|
||||
RotorOps.move_to_active_task_string = "activezone" --default string to search group names for the move to active zone task. requires ai_task_by_name
|
||||
RotorOps.shift_task_string = "shift"
|
||||
RotorOps.guard_task_string = "guard"
|
||||
--RotorOps.patrol_task_radius = 100 --patrol search radius
|
||||
--RotorOps.aggressive_task_radius = 1000 --aggressive search radius --not implementing for now until more time for testing
|
||||
RotorOps.defending_vehicles_behavior = "shift" --available options: 'none', 'patrol', 'shift'
|
||||
RotorOps.farp_pickups = true --allow ctld troop pickup at FARPs
|
||||
RotorOps.enable_staging_pickzones = true
|
||||
|
||||
---[[END OF OPTIONS]]---
|
||||
|
||||
@ -47,14 +63,13 @@ RotorOps.active_zone = "" --name of the active zone
|
||||
RotorOps.active_zone_index = 0
|
||||
RotorOps.game_state_flag = 1 --user flag to store the game state
|
||||
RotorOps.staging_zones = {}
|
||||
RotorOps.ctld_pickup_zones = {} --keep track of ctld zones we've added, mainly for map markup
|
||||
RotorOps.ai_defending_infantry_groups = {}
|
||||
RotorOps.ai_attacking_infantry_groups = {}
|
||||
RotorOps.ai_defending_vehicle_groups = {}
|
||||
RotorOps.ai_attacking_vehicle_groups = {}
|
||||
RotorOps.ai_tasks = {}
|
||||
RotorOps.defending = false
|
||||
RotorOps.staged_units_flag = 111
|
||||
RotorOps.staged_units_flag = 111 -- shows a percentage of the units found in the staging zone when the game starts. you can also use 'ROPS_ATTACKERS' for readability
|
||||
|
||||
trigger.action.outText("ROTOR OPS STARTED: "..RotorOps.version, 5)
|
||||
env.info("ROTOR OPS STARTED: "..RotorOps.version)
|
||||
@ -74,6 +89,13 @@ local cooldown = {
|
||||
["attack_plane_msg"] = 0,
|
||||
["trans_helo_msg"] = 0,
|
||||
}
|
||||
local zone_defenders_flags = {
|
||||
'ROPS_A_DEFENDERS',
|
||||
'ROPS_B_DEFENDERS',
|
||||
'ROPS_C_DEFENDERS',
|
||||
'ROPS_D_DEFENDERS',
|
||||
}
|
||||
RotorOps.farp_names = {}
|
||||
|
||||
|
||||
RotorOps.gameMsgs = {
|
||||
@ -425,14 +447,14 @@ function RotorOps.getValidUnitFromGroup(grp)
|
||||
else
|
||||
group_obj = grp
|
||||
end
|
||||
if not grp then
|
||||
if not group_obj then
|
||||
return nil
|
||||
end
|
||||
if grp:isExist() ~= true then
|
||||
if group_obj:isExist() ~= true then
|
||||
return nil
|
||||
end
|
||||
local first_valid_unit
|
||||
for index, unit in pairs(grp:getUnits())
|
||||
for index, unit in pairs(group_obj:getUnits())
|
||||
do
|
||||
if unit:isExist() == true then
|
||||
first_valid_unit = unit
|
||||
@ -443,7 +465,35 @@ function RotorOps.getValidUnitFromGroup(grp)
|
||||
return first_valid_unit
|
||||
end
|
||||
|
||||
--"static" in this case, is our groups/units that we don't want controlled by conflict zone tasks
|
||||
local function isStaticUnit(unit)
|
||||
local unit_obj
|
||||
if type(unit) == 'string' then
|
||||
unit_obj = Unit.getByName(unit)
|
||||
else
|
||||
unit_obj = unit
|
||||
end
|
||||
if string.find(unit_obj:getGroup():getName():lower(), RotorOps.exclude_ai_group_name:lower()) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--"static" in this case, is our groups/units that we don't want controlled by conflict zone tasks
|
||||
local function isStaticGroup(group)
|
||||
local group_obj
|
||||
if type(group) == 'string' then
|
||||
group_obj = Group.getByName(group)
|
||||
else
|
||||
group_obj = group
|
||||
end
|
||||
if string.find(group_obj:getName():lower(), RotorOps.exclude_ai_group_name:lower()) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
----USEFUL PUBLIC FUNCTIONS FOR THE MISSION EDITOR---
|
||||
|
||||
@ -500,21 +550,20 @@ end
|
||||
|
||||
|
||||
|
||||
--see list of tasks in aiExecute. Zone is optional for many tasks
|
||||
function RotorOps.aiTask(grp, task, zone)
|
||||
--see list of tasks in aiExecute. Zone/point is optional for many tasks. Works with group name or object/table
|
||||
function RotorOps.aiTask(grp, task, zone, point)
|
||||
local group_name
|
||||
if type(grp) == 'string' then
|
||||
group_name = grp
|
||||
else
|
||||
group_name = Group.getName(grp)
|
||||
end
|
||||
if string.find(group_name:lower(), RotorOps.exclude_ai_group_name:lower()) then --exclude groups that the user specifies with a special group name
|
||||
return
|
||||
end
|
||||
|
||||
if tableHasKey(RotorOps.ai_tasks, group_name) == true then --if we already have this group in our list to manage
|
||||
--debugMsg("timer already exists, updating task for "..group_name.." : ".. RotorOps.ai_tasks[group_name].ai_task.." to "..task)
|
||||
RotorOps.ai_tasks[group_name].ai_task = task
|
||||
RotorOps.ai_tasks[group_name].zone = zone
|
||||
RotorOps.ai_tasks[group_name].point = point
|
||||
else
|
||||
local vars = {}
|
||||
vars.group_name = group_name
|
||||
@ -522,8 +571,11 @@ function RotorOps.aiTask(grp, task, zone)
|
||||
if zone then
|
||||
vars.zone = zone
|
||||
end
|
||||
if point then
|
||||
vars.point = point
|
||||
end
|
||||
local timer_id = timer.scheduleFunction(RotorOps.aiExecute, vars, timer.getTime() + 5)
|
||||
RotorOps.ai_tasks[group_name] = {['timer_id'] = timer_id, ['ai_task'] = task, ['zone'] = zone}
|
||||
RotorOps.ai_tasks[group_name] = {['timer_id'] = timer_id, ['ai_task'] = task, ['zone'] = zone, ['point'] = point}
|
||||
end
|
||||
end
|
||||
|
||||
@ -541,23 +593,24 @@ function RotorOps.tallyZone(zone_name)
|
||||
|
||||
for index, unit in pairs(new_units) do
|
||||
if not hasValue(RotorOps.staged_units, unit) then
|
||||
if not isStaticUnit(unit) then
|
||||
env.info("RotorOps adding new units to staged_units: "..#new_units)
|
||||
table.insert(RotorOps.staged_units, unit)
|
||||
RotorOps.aiTask(unit:getGroup(),"move_to_active_zone", RotorOps.zones[RotorOps.active_zone_index].name)
|
||||
end
|
||||
else
|
||||
--env.info("unit already in table")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
--
|
||||
-- for index, unit in pairs(RotorOps.staged_units) do
|
||||
-- if string.find(Unit.getGroup(unit):getName():lower(), RotorOps.exclude_ai_group_name:lower()) then
|
||||
-- RotorOps.staged_units[index] = nil --remove 'static' units
|
||||
-- end
|
||||
-- end
|
||||
|
||||
end
|
||||
|
||||
--display a text message to all players with a radio sound effect
|
||||
function RotorOps.radioText(message)
|
||||
RotorOps.gameMsg({message, 'radio_effect.ogg'})
|
||||
end
|
||||
|
||||
|
||||
---AI CORE BEHAVIOR--
|
||||
@ -772,6 +825,138 @@ function RotorOps.patrolRadius(vars)
|
||||
end
|
||||
|
||||
|
||||
function RotorOps.shiftPosition(vars)
|
||||
--debugMsg("patrol radius: "..mist.utils.tableShow(vars.grp))
|
||||
local grp = vars.grp
|
||||
local search_radius = vars.radius or 100
|
||||
local inner_radius = 50 --minimum distance to move for randpointincircle
|
||||
local first_valid_unit
|
||||
if grp:isExist() ~= true then return end
|
||||
local start_point = vars.point
|
||||
|
||||
if not start_point then
|
||||
env.info("RotorOps: No point provided, getting current position.")
|
||||
for index, unit in pairs(grp:getUnits()) do
|
||||
if unit:isExist() == true then
|
||||
first_valid_unit = unit
|
||||
break
|
||||
else --trigger.action.outText("a unit no longer exists", 15)
|
||||
end
|
||||
end
|
||||
if first_valid_unit == nil then return end
|
||||
start_point = first_valid_unit:getPoint()
|
||||
end
|
||||
|
||||
|
||||
local max_waypoints = 2
|
||||
|
||||
local urban = RotorOps.pointIsUrban(start_point, 100)
|
||||
formation = 'Cone'
|
||||
if urban then
|
||||
formation = 'On Road'
|
||||
end
|
||||
|
||||
local path = {}
|
||||
path[1] = mist.ground.buildWP(start_point, '', 5)
|
||||
|
||||
for i = #path, max_waypoints, 1 do
|
||||
for i = 1, 4, 1 do
|
||||
local rand_point = mist.getRandPointInCircle(start_point, search_radius, inner_radius)
|
||||
|
||||
if mist.isTerrainValid(rand_point, {'LAND', 'ROAD'}) == true then
|
||||
path[#path + 1] = mist.ground.buildWP(rand_point, formation, 5)
|
||||
env.info("point is valid, adding as waypoint with formation: " .. formation)
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
mist.goRoute(grp, path)
|
||||
end
|
||||
|
||||
|
||||
function RotorOps.guardPosition(vars)
|
||||
--debugMsg("patrol radius: "..mist.utils.tableShow(vars.grp))
|
||||
local grp = vars.grp
|
||||
local search_radius = vars.radius or 100
|
||||
local first_valid_unit
|
||||
if grp:isExist() ~= true then return end
|
||||
local start_point = vars.point
|
||||
|
||||
if not start_point then
|
||||
env.info("RotorOps: No point provided, getting current position.")
|
||||
for index, unit in pairs(grp:getUnits()) do
|
||||
if unit:isExist() == true then
|
||||
first_valid_unit = unit
|
||||
break
|
||||
else --trigger.action.outText("a unit no longer exists", 15)
|
||||
end
|
||||
end
|
||||
if first_valid_unit == nil then return end
|
||||
start_point = first_valid_unit:getPoint()
|
||||
end
|
||||
local object_vol_thresh = 0
|
||||
local max_waypoints = 1
|
||||
local foundUnits = {}
|
||||
|
||||
local volS = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = grp:getUnit(1):getPoint(), --check if exists, maybe itterate through grp
|
||||
radius = search_radius
|
||||
}
|
||||
}
|
||||
|
||||
local ifFound = function(foundItem, val)
|
||||
--trigger.action.outText("found item: "..foundItem:getTypeName(), 5)
|
||||
if foundItem:hasAttribute("Infantry") ~= true then --disregard infantry...we only want objects that might provide cover
|
||||
if getObjectVolume(foundItem) > object_vol_thresh then
|
||||
foundUnits[#foundUnits + 1] = foundItem
|
||||
--trigger.action.outText("valid cover item: "..foundItem:getTypeName(), 5)
|
||||
else --debugMsg("object not large enough: "..foundItem:getTypeName())
|
||||
end
|
||||
else --trigger.action.outText("object not the right type", 5)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
world.searchObjects(1, volS, ifFound)
|
||||
world.searchObjects(3, volS, ifFound)
|
||||
world.searchObjects(5, volS, ifFound)
|
||||
--world.searchObjects(Object.Category.BASE, volS, ifFound)
|
||||
if #foundUnits > 0 then
|
||||
local path = {}
|
||||
path[1] = mist.ground.buildWP(start_point, '', 5)
|
||||
local rand_index = math.random(1,#foundUnits)
|
||||
path[#path + 1] = mist.ground.buildWP(foundUnits[rand_index]:getPoint(), '', 3)
|
||||
mist.goRoute(grp, path)
|
||||
end
|
||||
end
|
||||
|
||||
--helper function to try to determine a point is near many scenery objects
|
||||
function RotorOps.pointIsUrban(_point, _radius)
|
||||
local volS = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = _point,
|
||||
radius = _radius
|
||||
}
|
||||
}
|
||||
local foundUnits = {}
|
||||
local ifFound = function(foundItem, val)
|
||||
foundUnits[#foundUnits + 1] = foundItem
|
||||
end
|
||||
|
||||
world.searchObjects(5, volS, ifFound)
|
||||
--env.info("Found scenery objects: " .. #foundUnits)
|
||||
if #foundUnits > 10 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
|
||||
function RotorOps.aiExecute(vars)
|
||||
local update_interval = 60
|
||||
@ -780,6 +965,7 @@ function RotorOps.aiExecute(vars)
|
||||
local group_name = vars.group_name
|
||||
local task = RotorOps.ai_tasks[group_name].ai_task
|
||||
local zone = RotorOps.ai_tasks[group_name].zone
|
||||
local point = RotorOps.ai_tasks[group_name].point
|
||||
|
||||
-- if vars.zone then zone = vars.zone end
|
||||
|
||||
@ -862,6 +1048,20 @@ function RotorOps.aiExecute(vars)
|
||||
local speed = RotorOps.ground_speed
|
||||
local force_offroad = RotorOps.force_offroad
|
||||
mist.groupToPoint(group_name, RotorOps.active_zone, formation, final_heading, speed, force_offroad)
|
||||
elseif task == "shift" then
|
||||
local vars = {}
|
||||
vars.grp = Group.getByName(group_name)
|
||||
vars.radius = 250
|
||||
vars.point = point
|
||||
RotorOps.shiftPosition(vars) --takes a group object, not name
|
||||
update_interval = math.random(60,360)
|
||||
elseif task == "guard" then
|
||||
local vars = {}
|
||||
vars.grp = Group.getByName(group_name)
|
||||
vars.radius = 100
|
||||
vars.point = point
|
||||
RotorOps.guardPosition(vars) --takes a group object, not name
|
||||
update_interval = math.random(60,120)
|
||||
|
||||
end
|
||||
|
||||
@ -917,27 +1117,37 @@ function RotorOps.assessUnitsInZone(var)
|
||||
RotorOps.ai_attacking_infantry_groups = RotorOps.groupsFromUnits(attacking_infantry)
|
||||
RotorOps.ai_attacking_vehicle_groups = RotorOps.groupsFromUnits(attacking_vehicles)
|
||||
|
||||
|
||||
|
||||
for index, group in pairs(RotorOps.ai_defending_infantry_groups) do
|
||||
if group then
|
||||
if group and not isStaticGroup(group) then
|
||||
RotorOps.aiTask(group, "patrol")
|
||||
end
|
||||
end
|
||||
|
||||
for index, group in pairs(RotorOps.ai_attacking_infantry_groups) do
|
||||
if group then
|
||||
if group and not isStaticGroup(group) then
|
||||
RotorOps.aiTask(group, "clear_zone", RotorOps.active_zone)
|
||||
end
|
||||
end
|
||||
|
||||
for index, group in pairs(RotorOps.ai_attacking_vehicle_groups) do
|
||||
if group then
|
||||
if group and not isStaticGroup(group) then
|
||||
RotorOps.aiTask(group, "clear_zone", RotorOps.active_zone)
|
||||
end
|
||||
end
|
||||
|
||||
for index, group in pairs(RotorOps.ai_defending_vehicle_groups) do
|
||||
if group then
|
||||
if group and not isStaticGroup(group) then
|
||||
Group.getByName(group):getController():setOption(AI.Option.Ground.id.DISPERSE_ON_ATTACK , RotorOps.defending_vehicles_disperse)
|
||||
if RotorOps.defending_vehicles_behavior == "patrol" then
|
||||
RotorOps.aiTask(group, "patrol")
|
||||
elseif RotorOps.defending_vehicles_behavior == "shift" then
|
||||
local unit = RotorOps.getValidUnitFromGroup(group)
|
||||
if unit then
|
||||
RotorOps.aiTask(group, "shift", nil, unit:getPoint())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -953,13 +1163,20 @@ function RotorOps.assessUnitsInZone(var)
|
||||
|
||||
--sort infantry spawn zones and spawn quantity
|
||||
inf_spawn_zones = {}
|
||||
local total_spawn_zones = 0
|
||||
for zone, zoneobj in pairs(mist.DBs.zonesByName) do
|
||||
if string.find(zone, RotorOps.active_zone) and string.find(zone:lower(), "spawn") then --if we find a zone that has the active zone name and the word spawn
|
||||
inf_spawn_zones[#inf_spawn_zones + 1] = zone
|
||||
env.info("ROTOR OPS: spawn zone found:"..zone)
|
||||
end
|
||||
if string.find(zone:lower(), "spawn") then
|
||||
total_spawn_zones = total_spawn_zones + 1
|
||||
end
|
||||
end
|
||||
--RotorOps.inf_spawns_avail = RotorOps.inf_spawns_per_zone * RotorOps.inf_spawn_multiplier[RotorOps.active_zone_index]
|
||||
if total_spawn_zones > 0 then
|
||||
RotorOps.inf_spawns_avail = (RotorOps.inf_spawns_total / total_spawn_zones) * #inf_spawn_zones
|
||||
end
|
||||
RotorOps.inf_spawns_avail = RotorOps.inf_spawns_per_zone * #inf_spawn_zones
|
||||
|
||||
env.info("ROTOR OPS: zone activated: "..RotorOps.active_zone..", inf spawns avail:"..RotorOps.inf_spawns_avail..", spawn zones:"..#inf_spawn_zones)
|
||||
end
|
||||
@ -973,6 +1190,7 @@ function RotorOps.assessUnitsInZone(var)
|
||||
active_zone_initial_defenders = nil
|
||||
defenders_remaining_percent = 0
|
||||
trigger.action.setUserFlag(defenders_status_flag, 0) --set the zone's flag to cleared
|
||||
trigger.action.setUserFlag(zone_defenders_flags[RotorOps.active_zone_index], 0) --set the zone's flag to cleared
|
||||
if RotorOps.defending == true then
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.enemy_cleared_zone, RotorOps.active_zone_index)
|
||||
else
|
||||
@ -984,6 +1202,7 @@ function RotorOps.assessUnitsInZone(var)
|
||||
|
||||
else
|
||||
trigger.action.setUserFlag(defenders_status_flag, defenders_remaining_percent) --set the zones flag to indicate the status of remaining defenders
|
||||
trigger.action.setUserFlag(zone_defenders_flags[RotorOps.active_zone_index], defenders_remaining_percent)
|
||||
end
|
||||
|
||||
--are all zones clear?
|
||||
@ -998,13 +1217,14 @@ function RotorOps.assessUnitsInZone(var)
|
||||
--update staged units remaining flag
|
||||
local staged_units_remaining = {}
|
||||
for index, unit in pairs(RotorOps.staged_units) do
|
||||
if unit:isExist() then
|
||||
if unit:isExist() and unit:getLife() > 0 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)
|
||||
trigger.action.setUserFlag('ROPS_ATTACKERS', percent_staged_remain)
|
||||
debugMsg("Staged units remaining percent: "..percent_staged_remain.."%")
|
||||
|
||||
|
||||
@ -1013,9 +1233,11 @@ function RotorOps.assessUnitsInZone(var)
|
||||
if RotorOps.defending == true then
|
||||
RotorOps.game_state = RotorOps.game_states.lost
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.lost)
|
||||
trigger.action.setUserFlag('ROPS_GAMESTATE', RotorOps.game_states.lost)
|
||||
else
|
||||
RotorOps.game_state = RotorOps.game_states.won
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.won)
|
||||
trigger.action.setUserFlag('ROPS_GAMESTATE', RotorOps.game_states.won)
|
||||
end
|
||||
return --we won't reset our timer to fire this function again
|
||||
end
|
||||
@ -1030,6 +1252,7 @@ function RotorOps.assessUnitsInZone(var)
|
||||
if RotorOps.defending and defending_game_won then
|
||||
RotorOps.game_state = RotorOps.game_states.won
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.won)
|
||||
trigger.action.setUserFlag('ROPS_GAMESTATE', RotorOps.game_states.won)
|
||||
return --we won't reset our timer to fire this function again
|
||||
end
|
||||
|
||||
@ -1064,7 +1287,7 @@ function RotorOps.assessUnitsInZone(var)
|
||||
local function timedDeploy()
|
||||
if vehicle:isExist() then
|
||||
env.info(vehicle:getName().." is deploying troops.")
|
||||
RotorOps.deployTroops(4, vehicle:getGroup(), false)
|
||||
RotorOps.deployTroops(RotorOps.inf_apc_group, vehicle:getGroup(), false)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1086,9 +1309,9 @@ function RotorOps.assessUnitsInZone(var)
|
||||
local zone = inf_spawn_zones[rand_index]
|
||||
|
||||
if RotorOps.defending then
|
||||
ctld.spawnGroupAtTrigger("blue", 5, zone, 1000)
|
||||
ctld.spawnGroupAtTrigger("blue", RotorOps.inf_spawn_blue, zone, 1000)
|
||||
else
|
||||
ctld.spawnGroupAtTrigger("red", 5, zone, 1000)
|
||||
ctld.spawnGroupAtTrigger("red", RotorOps.inf_spawn_red, zone, 1000)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.infantry_spawned, math.random(1, #RotorOps.gameMsgs.infantry_spawned))
|
||||
end
|
||||
|
||||
@ -1120,12 +1343,19 @@ function RotorOps.assessUnitsInZone(var)
|
||||
local message = ""
|
||||
local header = ""
|
||||
local body = ""
|
||||
-- if RotorOps.defending == true then
|
||||
-- header = "[DEFEND "..RotorOps.active_zone .. "] "
|
||||
-- body = "RED: " ..#attacking_infantry.. " infantry, " .. #attacking_vehicles .. " vehicles. BLUE: "..#defending_infantry.. " infantry, " .. #defending_vehicles.." vehicles. ["..defenders_remaining_percent.."%]"
|
||||
-- else
|
||||
-- header = "[ATTACK "..RotorOps.active_zone .. "] "
|
||||
-- body = "RED: " ..#defending_infantry.. " infantry, " .. #defending_vehicles .. " vehicles. BLUE: "..#attacking_infantry.. " infantry, " .. #attacking_vehicles.." vehicles. ["..defenders_remaining_percent.."%]"
|
||||
-- end
|
||||
if RotorOps.defending == true then
|
||||
header = "[DEFEND "..RotorOps.active_zone .. "] "
|
||||
body = "RED: " ..#attacking_infantry.. " infantry, " .. #attacking_vehicles .. " vehicles. BLUE: "..#defending_infantry.. " infantry, " .. #defending_vehicles.." vehicles. ["..defenders_remaining_percent.."%]"
|
||||
body = "BLUE: "..#defending_infantry.. " infantry, " .. #defending_vehicles.." vehicles. RED CONVOY: " .. #staged_units_remaining .." vehicles. ["..percent_staged_remain.."%]"
|
||||
else
|
||||
header = "[ATTACK "..RotorOps.active_zone .. "] "
|
||||
body = "RED: " ..#defending_infantry.. " infantry, " .. #defending_vehicles .. " vehicles. BLUE: "..#attacking_infantry.. " infantry, " .. #attacking_vehicles.." vehicles. ["..defenders_remaining_percent.."%]"
|
||||
body = "RED: " ..#defending_infantry.. " infantry, " .. #defending_vehicles .. " vehicles. BLUE CONVOY: " .. #staged_units_remaining .." vehicles. ["..percent_staged_remain.."%]"
|
||||
end
|
||||
|
||||
message = header .. body
|
||||
@ -1169,28 +1399,48 @@ function RotorOps.drawZones() --this could use a lot of work, we should use tri
|
||||
trigger.action.textToAll(coalition, id + 100, point, color, text_fill_color, font_size, read_only, text)
|
||||
end
|
||||
|
||||
|
||||
for index, pickup_zone in pairs(RotorOps.ctld_pickup_zones)
|
||||
do
|
||||
for c_index, c_zone in pairs(ctld.pickupZones)
|
||||
do
|
||||
if pickup_zone == c_zone[1] then
|
||||
--debugMsg("found our zone in ctld zones, status: "..c_zone[4])
|
||||
local ctld_zone_status = c_zone[4]
|
||||
local point = trigger.misc.getZone(pickup_zone).point
|
||||
local radius = trigger.misc.getZone(pickup_zone).radius
|
||||
for index, cpz in pairs(ctld.pickupZones) do
|
||||
env.info("CTLD pickzone name: " .. cpz[1])
|
||||
pickup_zone = trigger.misc.getZone(cpz[1])
|
||||
if pickup_zone then
|
||||
env.info("found a ctld pickup zone")
|
||||
local ctld_zone_status = cpz[4]
|
||||
local point = pickup_zone.point
|
||||
local radius = pickup_zone.radius
|
||||
local coalition = -1
|
||||
local id = index + 150 --this must be UNIQUE!
|
||||
local color = {1, 1, 1, 0.5}
|
||||
local fill_color = {0, 0.8, 0, 0.1}
|
||||
local line_type = 5 --1 Solid 2 Dashed 3 Dotted 4 Dot Dash 5 Long Dash 6 Two Dash
|
||||
if ctld_zone_status == 'yes' or ctld_zone_status == 1 then
|
||||
--debugMsg("draw the pickup zone")
|
||||
env.info("pickup zone is active, drawing it to the map")
|
||||
trigger.action.circleToAll(coalition, id, point, radius, color, fill_color, line_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- for index, pickup_zone in pairs(RotorOps.ctld_pickup_zones)
|
||||
-- do
|
||||
-- for c_index, c_zone in pairs(ctld.pickupZones)
|
||||
-- do
|
||||
-- if pickup_zone == c_zone[1] then
|
||||
-- --debugMsg("found our zone in ctld zones, status: "..c_zone[4])
|
||||
-- local ctld_zone_status = c_zone[4]
|
||||
-- local point = trigger.misc.getZone(pickup_zone).point
|
||||
-- local radius = trigger.misc.getZone(pickup_zone).radius
|
||||
-- local coalition = -1
|
||||
-- local id = index + 150 --this must be UNIQUE!
|
||||
-- local color = {1, 1, 1, 0.5}
|
||||
-- local fill_color = {0, 0.8, 0, 0.1}
|
||||
-- local line_type = 5 --1 Solid 2 Dashed 3 Dotted 4 Dot Dash 5 Long Dash 6 Two Dash
|
||||
-- if ctld_zone_status == 'yes' or ctld_zone_status == 1 then
|
||||
-- --debugMsg("draw the pickup zone")
|
||||
-- trigger.action.circleToAll(coalition, id, point, radius, color, fill_color, line_type)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
|
||||
end
|
||||
@ -1211,17 +1461,17 @@ function RotorOps.setActiveZone(new_index)
|
||||
|
||||
if new_index ~= old_index then --the active zone is changing
|
||||
|
||||
if not RotorOps.defending then
|
||||
-- if not RotorOps.defending then
|
||||
|
||||
if old_index > 0 and RotorOps.apcs_spawn_infantry == false then
|
||||
ctld.activatePickupZone(RotorOps.zones[old_index].name) --make the captured zone a pickup zone
|
||||
end
|
||||
ctld.deactivatePickupZone(RotorOps.zones[new_index].name)
|
||||
end
|
||||
-- if old_index > 0 and RotorOps.apcs_spawn_infantry == false then
|
||||
-- ctld.activatePickupZone(RotorOps.farp_names[old_index]) --make the captured zone a pickup zone
|
||||
-- end
|
||||
-- ctld.deactivatePickupZone(RotorOps.farp_names[new_index])
|
||||
-- end
|
||||
|
||||
RotorOps.game_state = new_index
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, new_index)
|
||||
|
||||
trigger.action.setUserFlag('ROPS_GAMESTATE', new_index)
|
||||
if new_index > old_index then
|
||||
if RotorOps.defending == true then
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.enemy_pushing, new_index)
|
||||
@ -1290,6 +1540,28 @@ function RotorOps.setupCTLD()
|
||||
}
|
||||
|
||||
|
||||
--add to CTLD default pickzone names. This could be done in a loop but this should be more readable
|
||||
--pickupZones = { "Zone name or Ship Unit Name", "smoke color", "limit (-1 unlimited)", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", flag number (optional) }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "STAGING", RotorOps.pickup_zone_smoke, -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "STAGING_BASE", RotorOps.pickup_zone_smoke, -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "ALPHA_FARP", RotorOps.pickup_zone_smoke, -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "BRAVO_FARP", RotorOps.pickup_zone_smoke, -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "CHARLIE_FARP", RotorOps.pickup_zone_smoke, -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "DELTA_FARP", RotorOps.pickup_zone_smoke, -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "HELO_CARRIER", "none", -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "HELO_CARRIER_1", "none", -1, "no", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops1", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops2", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops3", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops4", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops5", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops6", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops7", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops8", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops9", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = { "troops10", RotorOps.pickup_zone_smoke, -1, "yes", 0 }
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
@ -1310,8 +1582,13 @@ function RotorOps.addZone(_name, _zone_defenders_flag)
|
||||
end
|
||||
table.insert(RotorOps.zones, {name = _name, defenders_status_flag = _zone_defenders_flag})
|
||||
trigger.action.setUserFlag(_zone_defenders_flag, 101)
|
||||
trigger.action.setUserFlag(zone_defenders_flags[1], 101)
|
||||
trigger.action.setUserFlag(zone_defenders_flags[2], 101)
|
||||
trigger.action.setUserFlag(zone_defenders_flags[3], 101)
|
||||
trigger.action.setUserFlag(zone_defenders_flags[4], 101)
|
||||
RotorOps.drawZones()
|
||||
RotorOps.addPickupZone(_name, RotorOps.pickup_zone_smoke, -1, "no", 2)
|
||||
local farp_name = _name .. "_FARP"
|
||||
RotorOps.farp_names[#RotorOps.farp_names + 1] = farp_name
|
||||
end
|
||||
|
||||
|
||||
@ -1320,7 +1597,7 @@ function RotorOps.addStagingZone(_name)
|
||||
trigger.action.outText(_name.." trigger zone missing! Check RotorOps setup!", 60)
|
||||
env.warning(_name.." trigger zone missing! Check RotorOps setup!")
|
||||
end
|
||||
RotorOps.addPickupZone(_name, RotorOps.pickup_zone_smoke, -1, "no", 0)
|
||||
|
||||
RotorOps.staging_zones[#RotorOps.staging_zones + 1] = _name
|
||||
end
|
||||
|
||||
@ -1348,6 +1625,7 @@ function RotorOps.setupConflict(_game_state_flag)
|
||||
RotorOps.game_state = RotorOps.game_states.not_started
|
||||
processMsgBuffer()
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.not_started)
|
||||
trigger.action.setUserFlag('ROPS_GAMESTATE', RotorOps.game_states.not_started)
|
||||
trigger.action.outText("ALL TROOPS GET TO TRANSPORT AND PREPARE FOR DEPLOYMENT!" , 10, false)
|
||||
if RotorOps.CTLD_sound_effects == true then
|
||||
local timer_id = timer.scheduleFunction(RotorOps.registerCtldCallbacks, 1, timer.getTime() + 5)
|
||||
@ -1355,8 +1633,7 @@ function RotorOps.setupConflict(_game_state_flag)
|
||||
end
|
||||
|
||||
|
||||
function RotorOps.addPickupZone(zone_name, smoke, limit, active, side)
|
||||
RotorOps.ctld_pickup_zones[#RotorOps.ctld_pickup_zones + 1] = zone_name
|
||||
function RotorOps.addPickupZone(zone_name, smoke, limit, active, side) --depreciated, don't use
|
||||
ctld.pickupZones[#ctld.pickupZones + 1] = {zone_name, smoke, limit, active, side}
|
||||
end
|
||||
|
||||
@ -1370,14 +1647,14 @@ function RotorOps.startConflict()
|
||||
--missionCommands.removeItem(commandDB['start_conflict'])
|
||||
--commandDB['clear_zone'] = missionCommands.addCommand( "[CHEAT] Force Clear Zone" , conflict_zones_menu , RotorOps.clearActiveZone)
|
||||
|
||||
RotorOps.staged_units = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), RotorOps.staging_zones)
|
||||
local units_found = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), RotorOps.staging_zones)
|
||||
|
||||
--filter out 'static' units
|
||||
-- for index, unit in pairs(RotorOps.staged_units) do
|
||||
-- if string.find(Unit.getGroup(unit):getName():lower(), RotorOps.exclude_ai_group_name:lower()) then
|
||||
-- RotorOps.staged_units[index] = nil --remove 'static' units
|
||||
-- end
|
||||
-- end
|
||||
for index, unit in pairs(units_found) do
|
||||
if not isStaticUnit(unit) then
|
||||
RotorOps.staged_units[#RotorOps.staged_units + 1] = unit
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if RotorOps.staged_units[1] == nil then
|
||||
@ -1387,23 +1664,35 @@ function RotorOps.startConflict()
|
||||
end
|
||||
|
||||
if RotorOps.staged_units[1]:getCoalition() == 1 then --check the coalition in the staging zone to see if we're defending
|
||||
--DEFENSE
|
||||
trigger.action.setUserFlag('ROPS_DEFENDING', 1)
|
||||
RotorOps.defending = true
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.start_defense)
|
||||
ctld.activatePickupZone(RotorOps.zones[#RotorOps.zones].name) --make the last zone a pickup zone for defenders
|
||||
for index, zone in pairs(RotorOps.staging_zones) do
|
||||
ctld.deactivatePickupZone(zone)
|
||||
end
|
||||
ctld.activatePickupZone(RotorOps.farp_names[#RotorOps.farp_names]) --make the last zone a pickup zone for defenders
|
||||
|
||||
else
|
||||
--OFFENSE
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.start)
|
||||
for index, zone in pairs(RotorOps.staging_zones) do
|
||||
ctld.activatePickupZone(zone)
|
||||
if RotorOps.enable_staging_pickzones then
|
||||
if trigger.misc.getZone("STAGING_BASE") then
|
||||
ctld.activatePickupZone("STAGING_BASE")
|
||||
else
|
||||
ctld.activatePickupZone("STAGING")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
RotorOps.setActiveZone(1)
|
||||
|
||||
if RotorOps.ai_task_by_name then
|
||||
RotorOps.taskByName()
|
||||
end
|
||||
|
||||
local id = timer.scheduleFunction(RotorOps.assessUnitsInZone, 1, timer.getTime() + 5)
|
||||
world.addEventHandler(RotorOps.eventHandler)
|
||||
end
|
||||
@ -1431,6 +1720,32 @@ function RotorOps.triggerSpawn(groupName, msg, resume_task)
|
||||
|
||||
end
|
||||
|
||||
---Search for group names containing key strings to assign AI tasks
|
||||
function RotorOps.taskByName()
|
||||
env.info("RotorOps searching for groups to taskByName")
|
||||
for group_name, data in pairs(mist.DBs.groupsByName) do
|
||||
if string.find(group_name:lower(), RotorOps.patrol_task_string:lower()) then
|
||||
RotorOps.aiTask(group_name, "patrol")
|
||||
env.info("Tasking " .. group_name .. " as patrol.")
|
||||
elseif string.find(group_name:lower(), RotorOps.aggressive_task_string:lower()) then
|
||||
RotorOps.aiTask(group_name, "aggressive")
|
||||
env.info("Tasking " .. group_name .. " as aggressive.")
|
||||
elseif string.find(group_name:lower(), RotorOps.move_to_active_task_string:lower()) then
|
||||
RotorOps.aiTask(group_name, "move_to_active_zone")
|
||||
env.info("Tasking " .. group_name .. " to move to active zone.")
|
||||
elseif string.find(group_name:lower(), RotorOps.shift_task_string:lower()) then
|
||||
RotorOps.aiTask(group_name, "shift")
|
||||
env.info("Tasking " .. group_name .. " to shift positions.")
|
||||
elseif string.find(group_name:lower(), RotorOps.guard_task_string:lower()) then
|
||||
RotorOps.aiTask(group_name, "guard")
|
||||
env.info("Tasking " .. group_name .. " to guard positions.")
|
||||
end
|
||||
end
|
||||
if RotorOps.ai_task_by_name_scheduler then
|
||||
local timer_id = timer.scheduleFunction(RotorOps.taskByName, nil, timer.getTime() + 120)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function RotorOps.spawnAttackHelos()
|
||||
RotorOps.triggerSpawn("Enemy Attack Helicopters", RotorOps.gameMsgs.attack_helos_prep, true)
|
||||
@ -1443,8 +1758,16 @@ end
|
||||
|
||||
|
||||
|
||||
function RotorOps.farpEstablished(index)
|
||||
function RotorOps.farpEstablished(index, trigger_zone)
|
||||
env.info("RotorOps FARP established at "..RotorOps.zones[index].name)
|
||||
if trigger_zone then
|
||||
if RotorOps.farp_pickups then
|
||||
ctld.activatePickupZone(trigger_zone)
|
||||
end
|
||||
if RotorOps.farp_smoke_color >= 0 and RotorOps.pickup_zone_smoke == 'none' then
|
||||
trigger.action.smoke(trigger.misc.getZone(trigger_zone).point , RotorOps.farp_smoke_color)
|
||||
end
|
||||
end
|
||||
timer.scheduleFunction(function()RotorOps.gameMsg(RotorOps.gameMsgs.farp_established, index) end, {}, timer.getTime() + 15)
|
||||
end
|
||||
|
||||
@ -1562,6 +1885,7 @@ function RotorOps.spawnTranspHelos(troops, max_drops)
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- USEFUL PUBLIC 'LUA PREDICATE' FUNCTIONS FOR MISSION EDITOR TRIGGERS (don't forget that DCS lua predicate functions should 'return' these function calls)
|
||||
|
||||
--determine if any human players are above a defined ceiling above ground level. If 'above' parameter is false, function will return true if no players above ceiling
|
||||
|
||||
@ -22,6 +22,18 @@ spencershepard (GRIMM):
|
||||
-damage model for ground units that will disable their weapons and ability to move with partial damage before they are killed
|
||||
-added options table to allow easy adjustments before release
|
||||
-general refactoring and restructure
|
||||
|
||||
31 December 2021
|
||||
spencershepard (GRIMM):
|
||||
-added many new weapons
|
||||
-added filter for weapons.shells events
|
||||
-fixed mission weapon message option
|
||||
-changed default for damage_model option
|
||||
|
||||
16 April 2022
|
||||
spencershepard (GRIMM):
|
||||
added new/missing weapons to explTable
|
||||
added new option rocket_multiplier
|
||||
--]]
|
||||
|
||||
----[[ ##### SCRIPT CONFIGURATION ##### ]]----
|
||||
@ -40,6 +52,7 @@ splash_damage_options = {
|
||||
["infantry_cant_fire_health"] = 90, --if health is below this value after our explosions, set ROE to HOLD to simulate severe injury
|
||||
["debug"] = false, --enable debugging messages
|
||||
["weapon_missing_message"] = false, --false disables messages alerting you to weapons missing from the explTable
|
||||
["rocket_multiplier"] = 1.3, --multiplied by the explTable value for rockets
|
||||
}
|
||||
|
||||
local script_enable = 1
|
||||
@ -156,8 +169,14 @@ explTable = {
|
||||
["AB_250_2_SD_2"] = 100, --("AB 250-2 - 144 x SD-2, 250kg CBU with HE submunitions")
|
||||
["AB_250_2_SD_10A"] = 100, --("AB 250-2 - 17 x SD-10A, 250kg CBU with 10kg Frag/HE submunitions")
|
||||
["AB_500_1_SD_10A"] = 213, --("AB 500-1 - 34 x SD-10A, 500kg CBU with 10kg Frag/HE submunitions")
|
||||
--["LTF_5B"] = 100, --("LTF 5b Aerial Torpedo")
|
||||
--agm-65??
|
||||
["AGM_114K"] = 10,
|
||||
["HYDRA_70_M229"] = 8,
|
||||
["AGM_65D"] = 130,
|
||||
["AGM_65E"] = 300,
|
||||
["AGM_65F"] = 300,
|
||||
["HOT3"] = 15,
|
||||
["AGR_20A"] = 8,
|
||||
["GBU_54_V_1B"] = 118,
|
||||
|
||||
}
|
||||
|
||||
@ -253,9 +272,11 @@ function track_wpns()
|
||||
trigger.action.explosion(impactPoint, getWeaponExplosive(wpnData.name))
|
||||
--trigger.action.smoke(impactPoint, 0)
|
||||
end
|
||||
--if wpnData.cat == Weapon.Category.ROCKET then
|
||||
blastWave(impactPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, getWeaponExplosive(wpnData.name))
|
||||
--end
|
||||
local explosive = getWeaponExplosive(wpnData.name)
|
||||
if splash_damage_options.rocket_multiplier > 0 and wpnData.cat == Weapon.Category.ROCKET then
|
||||
explosive = explosive * splash_damage_options.rocket_multiplier
|
||||
end
|
||||
blastWave(impactPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, explosive)
|
||||
tracked_weapons[wpn_id_] = nil -- remove from tracked weapons first.
|
||||
end
|
||||
end
|
||||
|
||||
BIN
sound/embedded/radio_effect.ogg
Normal file
BIN
sound/embedded/radio_effect.ogg
Normal file
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,24 +0,0 @@
|
||||
## Forces Templates
|
||||
|
||||
The friendly/enemy forces templates available in the generator are simply .miz files in the Generator/Forces folder.
|
||||
|
||||
A Forces template defines the groups of ground units available, AI aircraft, liveries, and loadouts.
|
||||
|
||||
To create your own Forces template:
|
||||
|
||||
1) Create an empty mission on Caucasus
|
||||
2) Add ground unit groups.
|
||||
3) Save the mission in this directory.
|
||||
|
||||
Optional:
|
||||
|
||||
4) Add helicopters with "CAS" main task for attack helicopters.
|
||||
5) Add helicopters with "Transport" main task for transport helicopters.
|
||||
6) Add planes with "CAS" main task for attack planes.
|
||||
7) Add planes with "CAP" main task for fighters.
|
||||
8) Configure loadouts, liveries, and skill for aircraft.
|
||||
|
||||
Tips:
|
||||
- The mission generator will only extract blue ground units from the template when selected from the "Blue Forces" menu, and vice versa.
|
||||
- Only unit types are used from ground units. Liveries or other attributes are able to be copied.
|
||||
- For aircraft, group size is currently capped at 2 units per group to help prevent issues with parking. Only the first unit in the group is used as a source.
|
||||
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.
@ -1,20 +0,0 @@
|
||||
## Imports
|
||||
|
||||
A breakthrough feature of mission design with RotorOps is the ability to import complex arrangements of statics and vehicles. This allows you to create reusable mission assets like bases, FARPs, objective sites, or just about anything you can imagine building in the mission editor. You can place these on any map, at your desired point, rotation and coalition!
|
||||
|
||||
A selection of FOBs, FARPs, and other objects are available in the Imports folder. Included are multiplayer FOBs with up to 16 helicopter spawns! A guide to the included templates is available here: [RotorOps IMPORT Assets](http://dcs-helicopters.com/wp-content/uploads/2022/03/RotorOps_IMPORT_TEMPLATES-1.pdf)
|
||||
|
||||
|
||||
To use an import template:
|
||||
1) Place a static mark flag in the scenario template mission.
|
||||
2) Change the group name to 'IMPORT-filename', where the filename is a .miz file in the Generator/Imports folder.
|
||||
3) Change the flag coalition to CJTF Blue/Red or UN Peacekeepers.
|
||||
4) Change the flag heading and position as desired.
|
||||
5) Change the flag UNIT NAME to something relevant (ie. 'North Base')
|
||||
6) For multiple imports of the same template, the import object GROUP NAME should end with '-01' etc
|
||||
|
||||
To create a new import template:
|
||||
1) Make an empty mission on Caucasus.
|
||||
2) Place units/objects on the map.
|
||||
3) Make one unit group name: 'ANCHOR' This will represent the point of insertion/rotation in the target mission.
|
||||
4) Save the template .miz file in Generator/Imports
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user